C++程序设计重点总结(谭浩强版)
免费分享超过1000本计算机类电子书,包含编程语言、大数据、机器学习、校招面试经验等
()
文章目录
- 第1章 C++初步知识
- 1.流程
- 第3章 程序设计
- 1.优先级
- 2.switch
- break continue
- 第4章 函数
- 1.内置函数
- 2.函数重载
- 3.函数模板
- 4.带默认参数的函数
- 5.动态存储和静态存储
- 6.auto(自动变量、动态存储)
- 7.static(静态局部变量、静态存储)
- 8.register(寄存器变量、在内存)
- 9.extern(外部变量、静态存储、别的文件可引用)
- 10.static(静态外部变量、静态存储、只限本文件中使用)
- 11.内部函数外部函数
- 第5章 数组
- 1.一维数组
- 2.二维数组
- 3.字符数组
- 4.字符串处理函数
- 5.string
- 6.string []
- 第6章 指针和引用
- 1.指针 和 指针变量
- 2.`&` `*`
- 3.指针作为形参
- 4.指向数组
- 5.指针变量 和 指针函数
- 6.指针数组
- 7.const指针
- * void指针
- 8.指针小结
- 9.引用(本质是指针常量)
- 10.参数传递
- 第7章 用户自定义数据类型
- 1.结构体类型(`struct`)
- 2.链表
- 3.`new`和`delete`
- 4.枚举类型(枚举常量)
- 5.typedef声明新类型名
- 第9章 类和对象
- 1.构造函数
- 2.析构函数
- 3.构造函数、析构函数执行顺序
- 4.对象指针
- 5.this指针
- 6.共享数据的保护
- 7.指向对象的常指针
- 8.指向常对象的指针变量
- 9.对象的常引用
- 10.对象的动态建立和释放
- 11.1 对象赋值
- 11.2 对象复制
- 13.静态成员(static)
- 14.友元
- 友元函数
- 友元类
- 15.类模板
- 第10章 运算符重载(不在考纲内)
- 1.运算符重载规则
- 2.运算符重载函数作为类成员函数
- 3.运算符重载函数作为友元函数
- 4.单目运算符重载
- 5.重载流运算符(必须是友元函数)
- 6.转换构造函数
- 7.类型转换函数
- 第11章 继承与派生
- 1.派生类构成
- 2.权限
- 3. 派生类的构造函数
- 4. 派生类的析构函数
- 5.多重继承
- 6.虚基类
- 7.基类和派生类的转换
- 8.小结
- 第12章 多态性和虚函数
- 1.静态多态和动态多态
- 2.虚函数(=基类声明虚函数+基类对象指针调用派生类方法)
- 3.虚析构函数
- 4.纯虚函数
- 5.抽象类
- 6.静态关联和动态关联
- 第13章 输入输出流
- 1.C++流库
- 2.标准输出流(ostream类的3个对象)
- 3.格式控制
- 4.标准输入流(cin)
- 5.字符输入的成员函数
- 6.istream类的其他成员函数
- 7.文件
- 8.文件流类和文件流对象
- 9.对ASCII码文件的操作
- 10.对二进制文件的操作
- 11.文件指针有关函数
第1章 C++初步知识
1.流程
编辑 -> 编译(.cpp) -> 连接(.obj) -> 运行(.exe) -> 分析结果
第3章 程序设计
1.优先级
! > 算术运算符 > 关系运算符 > &&和|| > 赋值运算符
2.switch
switch(表达式)
{case 常量表达式1: 语句1; break;case 常量表达式2: 语句2; break;...default: 语句; break;
}
break continue
while(表达式1)
{if()continue; //结束本次循环,继续下次循环if()break; //结束全部循环
}
- 表达式可以是数值类型包括字符类型数据
- 次序不影响结果
- case表达式的值必须互不相同
第4章 函数
C++在程序进行编译时,以程序文件模块为编译单位
1.内置函数
在编译时将调用函数的代码直接嵌入到主调函数中,而不是将流程转出去
inline int max(int, int, int);inline int max(int a, int b, int c)
{...
}
- 规模小
- 使用频繁
- 不包含复杂控制语句,如循环语句和switch语句
- 建议性,而非指令性
2.函数重载
重载函数的参数格式、参数类型、参数顺序三者中必须至少有一种不同,返回值类型可同可不同
3.函数模板
函数模板只适用于函数体相同、函数的参数相同而类型不同的情况
如果参数的个数不同,则不能用函数模板。
template <typename T>
T max(T a, T b, T c)
{...
}
4.带默认参数的函数
- 实参和形参的结合是从左至右,指定默认值的参数必须放在形参列表的最右边
- 声明中给出默认值,定义时可以省略
- 一个函数不能既作为重载函数,又作为有默认参数的函数
5.动态存储和静态存储
- 动态存储:动态分配空间
- 静态存储:分配固定空间
6.auto(自动变量、动态存储)
int f(int a)
{auto int b, c = 3; //自动变量,默认为auto,可省略
}
- f函数被调用结束后,变量被销毁
- 不赋初值,则它的值是个不确定的值
7.static(静态局部变量、静态存储)
int f(int a)
{static int b = 0; //静态局部变量
}
- f函数被调用结束后,变量保留,外边不可以调用
- 不赋初值,编译时自动赋值0或空字符
8.register(寄存器变量、在内存)
int f(int a)
{register int i=1; //寄存器变量
}
- 使用频繁的可以声明为寄存器变量
- 建议性,而非强制性
9.extern(外部变量、静态存储、别的文件可引用)
1.在一个文件内声明全局变量
int main()
{extern int a, b; //全局变量的提前引用声明cout<<a<<b<<endl;
}
int a = 15, b = -8; //全局变量
2.在多文件的程序中声明外部变量
file1.cpp
int a=3, b=4;
...
file2.cpp
extern int a, b; //在编译连接成一个程序后,将file1.cpp中的a、b作用域扩展到file2.cppint main()
{cout<<a<<" "<<b<<endl;
}
10.static(静态外部变量、静态存储、只限本文件中使用)
file1.cpp
static int a = 3; //静态外部变量,只限于本文件中用
file2.cpp
extern int a; //引用不到file2.cpp中的a,因为是静态外部变量
int main()
{cout<<a<<endl;
}
11.内部函数外部函数
- 内部函数:函数只能被本文件中的其他函数调用
static int func(int a, int b)
{...
}
- 外部函数:可供其他函数调用,默认都是外部函数
file1.cpp
int max(int x, int y)
{...
}
file2.cpp
int main()
{extern int max(int, int); //声明要调用其他文件中的max函数max(1, 2);
}
第5章 数组
- 字符串结束标志符
\0
(ASCII码为0)
1.一维数组
int a[10]; //不允许对数组的大小作动态定义const int n = 5;
int a[n]; //合法,因为n是常量
2.二维数组
int a[3][4];
int a[][4] = {1, 2, 3, 4, 5, 6}//第2维的长度不能省略
3.字符数组
char a[10] = {'I', 'a', 'm', 'h', 'a', 'p', 'p', 'y'};
char c[] = "I am happy"; //正确
c = a; //错误
c = {'I', 'a', 'm', 'h', 'a', 'p', 'p', 'y'}; //错误
c[0] = 'I'; //正确char str[20];
cin>>str; //输入字符串
cout<<str; //输出字符串 遇到'\0'结束,不包括'\0'
4.字符串处理函数
#include <string.h>
或
#include <string>
strcat
:字符串连接函数
strcat()
strcpy
:字符串复制函数(覆盖)
char str1[10], str2[] = "China";
strcpy(str1, str2); //第1个实参必须是数组名,第2个实参可以是数组名,也可以是字符串常量
strcpy(str1, "China");strcpy(str1, str2, 2); //将str2中前面2个字符复制到str1中去,然后加上'\0'
strcmp
:字符串比较函数
strcmp(str1, str2); //正确
strcmp("China", "Korea"); //正确
strcmp(str1, "Beijing"); //正确
串1 == 串2,返回0
串1 > 串2,返回正数
串1 < 串2,返回负数
strlen
:字符串长度函数
char str[10] = "China";
cout<<strlen(str); //5 不包含'\0'
5.string
string不是C++本身的基本类型,而是C++标准库中声明的一个字符串类
- 需要引入头文件
#include <string>
- 定义
string s1, s2;
s1 = "Hello";
s2[0] = 'a'; //合法修改第一个字符
- 输入输出
cin>>s1;
cout<<s1;
- 运算
string s1, s2;
s1 = "Hello";
s2 = s1; //赋值运算
s2 = s1 + s2; //连接
if(s1 > s2) ...; //比较运算符
6.string []
string name[3] = {"Zhang", "Li", "Sun"};
第6章 指针和引用
1.指针 和 指针变量
- 变量的指针:变量的地址
- 指针变量:一个变量专门存放地址(指针)
2.&
*
自右向左结合
int a = 10;
int *pointer_1, *pointer_2;
pointer_1=&a;
pointer_2 = &*pointer_1; //从右向左执行,将a的地址赋给pointer_2
3.指针作为形参
调用函数时不会改变实参指针变量的值(地址),但可改变实参指针变量所指向的值
int a = 10
int *p = a;
fun(p);
int fun(int *p){} //不改变p的值,但可改变a的值
4.指向数组
启示指向的是数组的第一个元素
int a[10];
int *p;p=&a[0]; //等价于
p=a;p+1 //表示数组的下一个元素void select_sort(int array[], int n) //等价于
void select_sort(int *array, int n)
5.指针变量 和 指针函数
- 指针变量(2个括号)【是变量】
int a=1, b=1;
int max(int x, int y);int (*p)(int, int); // 是变量,可以指向函数
p=max
p(a, b) //用函数指针调用 等价于
max(a, b)
- 指针函数(1个括号)【是函数】
int *p(int x, int y) {} // 是函数,返回值为int型指针
6.指针数组
int *p[4]; //每个元素都是指针类型
相当于:
int *p0; int *p1; int *p2; int *p3;例:
char *name[] = {"peter", "jack", "smith"}; //指针数组
[]
优先级比*
高
注意:
int (*p)[4]; //指向一维数组的指针变量 用来指向二维数组
#include<iostream>
using namespace std;int main()
{int w[3][4] = {{1,2,3, 11}, {4,5,6, 12}, {7,8,9, 13}};int (*p)[4] = w; //指向一维数组的指针变量for(int i=0; i<3; i++){for(int j=0; j<4; j++)cout<<p[i][j]<<" ";cout<<endl;}return 0;
}
7.const指针
- 指向常量的指针变量(量不能变)
const int *p = &a; // 不允许通过p修改a的值
*p = 10; //非法
p = &b; //合法
- 常指针(指针不能变)
int * const p = &a; //不允许修改p的指向,但允许修改a的值;定义时要初始化
*p = 10; //合法
p = &b; //非法
- 指向常量的常指针(量不能变,指针也不能变)
1.常变量只能由 [指向常变量的指针变量] 指向它,不能用 [一般指针]
2.[指向常变量的指针变量] 也可以指向[一般变量],那么用该指针变量访问时有常变量的特征
3.[一般指针]只能指向[一般变量]
const int * const p = &a;
*p = 10; //非法
p = &b; //非法
* void指针
- 不指向任何类型 = 指向空类型 = 指向不确定的类型
可以把
非void型
的指针赋给void型
指针变量,但不能把void型
指针直接赋给非void型
,必须先进行强制转换
int a = 3;
int *p1 = &a;
char *p2 = "new";
void *p3;
p3 = (void *)p1; //将p1的值转换为void *类型,赋给p3
cout<<*p3<<endl; //p3不能执行确定类型的变量,非法
cout<<p3<<endl; //a的纯地址,但p3并不指向acout<<*(int *)p3<<endl; //把p3的值转换为(int *)型,可以指向变量a
int main()
{int a = 3;int *p1 = &a;void *p2 = p1;cout<<p2<<endl; //0x6ffe44 p2是a的纯地址,但p2并不指向a,所以*p2是非法的 cout<<&a<<endl; //0x6ffe44 a的地址 cout<<&p2<<endl; //0x6ffe38 p2指针变量自己的地址 return 0;
}
8.指针小结
定义 | 类型 | 含义 |
---|---|---|
int *p; | int * | p为指针变量,指向int型数据 |
int *p[4]; | int *[4] | p为指针数组,指向4个元素都是int型指针的数组 |
int (*p)[4]; | int (*)[4] | p为指针变量,指向4个int数据的一维数组 |
int *p (); | int * () | p为正常函数,只是函数返回值为int型指针 |
int (*p)(); | int (*)() | p为指针变量,指向函数 |
int **p; | int ** | p为指针变量,指向一个指针(该指针指向int型数据) |
int * const p | int * const | p是指针变量,常指针,指向不能变 |
const int *p int const * p | const int * | p是指针变量,指向常量,不能通过p修改值 |
const int * const p | const int * const | p是指针变量,指向不能变且也不能通过p修改值 |
void *p | void * | p是指针变量,基类型为空(void),不指向具体对象 |
9.引用(本质是指针常量)
变量的“别名”
int a;
int &b = a; /* 引用只有声明,没有定义 */int &b; /* 非法!引用声明的同时必须初始化 */int a1, a2;
int &b = a1;
int &b = a2; /* 非法!引用声明后不能变更 */int a[5];
int &b[5] = a; //非法!不能建立引用数组
int &c[5] = a[0]; //非法!不能作为数组元素的别名int a;
int &b = a;
int *p = b; //非法!不能建立指向引用的指针int a;
int &b = a;
int *p = &b; //合法,&b是a的地址
10.参数传递
- 变量名作形参(值传递)
void swap(int a, int b) {...
}void main(){int i=1, j=2;swap(i, j);
}
- 指针作形参(值传递)
void swap(int *p1, int *p2){...
}void main(){int i=1, j=2;swap(&i, &j);
}
- 引用作形参(地址传递)
void swap(int &a, int &b){...
}void main(){int i=1, j=2;swap(i, j);
}
第7章 用户自定义数据类型
1.结构体类型(struct
)
struct Student
{int num;char name[20];char sex;
} student1 = {1001, "Zhangsan", 'M'}; //最后一个分号不能少!!!Student student2 = {1002, "Wangwu", 'F'};student1 = student2; //相同结构体类型变量可相互赋值student.num = 10010; //"."是成员运算符Student stu[3] = {1001, "Jack", 'M', 1002, "Peter", 'F', 1003, "mali", 'M'}; //结构体数组Student *p = &student1; //结构体指针等价
student1.num;
(*p).num; // "." > "*"
p->num; // "->" > "++"
p->num++; //得到num后,使用完加1
++p->num; //等到num,加1后使用
2.链表
见考试大纲
3.new
和delete
int *p1 = new int(100); //整型数
int *p2 = new int[10]; //整型数组delete p1; //释放变量
delete [] p2; //释放数组
4.枚举类型(枚举常量)
enum weekday{sun=7, mon=1, tue, wed, thu, fri, sat}; //tue为2,wed为3...往后加1Weekday workday;workday = tue; //正确
workday = 2; //错误
workday = (Weekday)2; //C语言风格强制类型转化
workday = Weekday(2); //C++风格强制类型转化
5.typedef声明新类型名
typedef int INTEGER;
INTERGER I;typedef int NUM[100]; //声明NUM为整型数组类型,包含100元素
NUM n;typedef char *STRING; //声明STRING为char *类型,即字符指针类型
STRING p;typedef int (*POINTER)(); //声明POINTER为指向函数的指针,函数返回整数值
POINTER P;typedef struct
{int month;int day;int year;
} DATE; //声明DATE为结构体类型
DATE birthday;
第9章 类和对象
1.构造函数
格式:
class Student
{private:int num;int score;char name[20];public:Student(){} //无参构造函数Student(int n) //一个参数的构造函数{num = n;score = 0;strcpy(name, "123");}Student(char nam[], int n=0, int score=0) //默认参数的构造函数{num = n;score = n;strcpy(name, nam);}Student(int n, int s, char nam[]):num(n), score(s){strcpy(name, nam);} //参数初始化列表}
注意项:
- 构造函数名必须与类名同名
- 无返回值,无类型
- 自动调用,用户不能显式调用
Student s; //正确
Student s(); //错误!不能显式调用
- 系统可自动生成无参构造函数
- 数据成员是数组时,不能用参数初始化列表,要用正常函数体初始化
- 带默认参数的构造函数,默认值在声明时给出
Student(int = 1001, int = 20); //省略形参名声明合法
- 一个类只能有一个默认构造函数
//下面两同时存在是错误的!二义性
Stduent();
Student(int num=10, int score=0);Student s; //产生二义性
- [构造函数重载] 和 [带默认参数的构造函数] 不能同时存在
//产生二义性
Box();
Box(int, int);
Box(int =10, int =10, int =10);
2.析构函数
4种情况下执行析构函数:
- 函数内定义的局部对象:函数调用结束时释放前执行
- 静态局部对象:main函数结束或exit函数结束时调用
- 全局对象:main函数结束或exit函数结束时调用
- new运算符动态建立的对象:在delete运算符释放该对象之前
析构函数不是删除对象,只是在撤销对象占用的内存之前完成一些清理工作
- 析构函数没有返回值;没有函数类型;没有函数参数;不能被重载。
- 一个类只能由一个析构函数
- 编译系统会自动生成一个析构函数(do nothing)
3.构造函数、析构函数执行顺序
先构造的后析构,后构造的先析构
4.对象指针
Time *pt;
Time t1;
pt = &ti;//等价于
(*pt).hour;
pt->hour; // "->"是指针专用的
成员函数指针变量:
void (Time::*p2)();
p2=&Time::get_time;
要求三个匹配
- 函数参数的类型和参数个数
- 函数返回值的类型
- 所属的类
5.this指针
每个成员函数都包含一个特殊的指针,指向本类对象的指针,他的值是当前被调用的成员函数所在的对象的起始地址。
//等价于
(*this).height
this->height
height
优先级:"." > “*”
6.共享数据的保护
常对象
//等价
Time const t1(12, 34, 46);
const Time t1(12, 34, 46);
- 常对象必须初始化,整个对象生命周期中,数据成员的值不能改变
- 常对象只能调用常成员函数
- 常成员函数可以访问常对象中的数据成员,但不许改变他的值
- 一定要改常对象中的某个数据成员值
mutable int count; //mutable 可变数据成员
常数据成员(访问不限,不可改值)
class Time
{const int hour;
}
- 只能通过构造函数的参数初始化表初始化
- 任何其他函数都不能改变它的值
常成员函数(访问不限,不可改值)
class Time
{void get_time() const;
}
- 只能引用本类的数据成员,但不能改变值
- 不能调用另一个非const的成员函数
7.指向对象的常指针
Time t1(19 ,12, 15);
Time * const p1;
p1 = &t1;
- 指向对象的常指针,指向后,不允许改变指向
- 但可以改变对象的数据成员
- 做形参使用:保护指向在函数运行期不允许改变指向
void fun(Time * const t);
8.指向常对象的指针变量
同 指向常变量的指针变量
- 常对象只能由[指向常对象的指针变量]指向,不能由[一般指针]指向
- 使用[指向常对象的指针变量]时,有常量特征,不能改变值
- 做形参使用:保护指向的对象在执行中不改变值
void fun(const Time *t);
9.对象的常引用
void fun(const Time &t); //t所指向的变量的值不能变
10.对象的动态建立和释放
Time *t = new Time;
delete t; //在释放内存空间前,自动调用析构函数
11.1 对象赋值
Time t1, t2;
...
t1 = t2; //同属一类的两个对象可相互赋值
- 类的数据成员中不能包括动态分配的数据,否则赋值有问题
11.2 对象复制
Box::Box(const Box&b) //复制构造函数 注意参数是引用!!
{height = b.height;width =b.width;
}...Box box2(box1); //对象复制
- 复制构造函数:只有1个参数,一般是const对象引用
- 编译系统自动提供一个复制构造函数
对象复制 | 对象赋值 |
---|---|
从无到有建立新对象 | 对已存在的对象进行赋值 |
对象复制的使用场景:
- 1.建立新对象
Box box1(12, 15, 16);
Box box2(box1); //建立新对象box2
- 2.函数参数为类对象
void fun(Box b) //函数调用时的虚实结合
{...
}
- 3.函数的返回值是类的对象
Box f()
{Box box(12, 2, 4);return box; //赋值给一个“临时对象”返回给函数调用处
}int main()
{Box b = f();return 0;
}
13.静态成员(static)
- 归属于类,所有该类的对象所共有
class Box
{public:static int height; //静态数据成员staitc int volume(); //静态成员函数
}int Box::height = 10; //静态数据成员只能在类体外进行初始化int Box::volume()
{return height;
}int main()
{Box a;//静态成员a.height; //可以,通过对象引用Box::height; //也可以,通过类名引用//静态成员函数a.volume(); Box::volume();return 0;
}
静态成员
- 内存中只占一份空间,程序运行开始时分配空间
- 静态数据成员只能在类体外进行初始化
- 静态数据成员未赋初值,默认赋0
- 静态数据成员可被对象名引用,也可通过类名引用
- 静态数据成员同样受访问权限控制
静态成员函数
- 因:静态成员函数没有
this
指针 - 果:静态成员函数不能直接访问本类非静态成员
- 一定要访问非静态成员,通过
对象名.非静态成员函数
14.友元
友元函数
- 友元函数可以访问该类的私有成员
1.普通函数作为友元函数
class Time
{private:int hour; public:friend void display(Time &); //友元函数(不属于该类)
}void display(Time &t) //普通函数
{t.hour; //可以通过对象名访问私有成员
}
- 引用私有成员要用对象名.数据成员
- 没有
this
指针
2.一个类的成员函数,作为另一个类的友元函数
class Date; //提前引用声明/* 客人 */
class Time
{private:int hour;public:void display(Date &); //正常成员函数
}/* 主人 */
class Date
{private:int hour;public:friend void Time::display(Date &); //Time类中的display函数作为本类的友元函数
}void Time::display(Date &d)
{d.hour; // 别人家的私有数据成员hour; //自己家的私有数据成员
}
注意项:
- 声明友元函数时,加上该函数所属的类
- 引用别人地私有成员要通过
对象名.数据成员
友元类
//B是A的友元类,B中的所有成员函数都是A的友元函数
class B;
class A
{ public:friend class B; //B是A的友元类
}class B
{...
}
- 友元关系不能被继承
- 友元关系是单向的,且不具有传递性
15.类模板
功能相同,只是数据类型不同
template <class T>
class Compare
{public:Compare(T a, T b) //构造函数{x=a;y=b;}T max() //成员函数类内定义 { return (x>y)?x:y; }T min();private:T x, y;
}template<class T>
T Compare<T>::min() //类外定义
{ return (x>y)?x:y;}int main()
{Compare<int> f1(3, 7); Compare<double> f2(45.2, 30.1);return 0;
}
三部曲:
- 1.写个实际的类
- 2.虚拟类名替换具体类名
- 3.第一行加入
template <class 虚拟类名>
多个类型参数的情况:
template <class T1, class T2>
class Box
{...
}
类模板 | 模板类 |
---|---|
重点是模板,产生类的模子 | 重点是类,由模板产生的类 |
第10章 运算符重载(不在考纲内)
1.运算符重载规则
- 运算符重载实质是函数重载
int operator+ (int a, int b)
{return (a+b);
}
- 不能重载的5个运算符
. * :: sizeof ?:
- 重载不能改变运算符运算对象(即操作数)个数
- 重载不能改变运算符的优先级别
- 重载不能改变运算符的结合性
- 重载运算符的函数不能有默认的参数
- 重载运算符必须至少一个参数是类的对象(或是类对象的引用),不能都是C++标准类型,以防止用户修改用于标准类型数据的运算符的性质
- 用于类对象的运算符一般必须重载,除了“=” “&”
2.运算符重载函数作为类成员函数
第一个参数是this指针隐式调用的访问
class Complex
{private:double real;double imag;public:Complex operator+(Complex &c2); //成员函数运算符重载函数
};Complex Complex::operator+(Complex &c2)
{Complex c;c.real = real + c2.real;c.imag = imag + c2.imag;return c;
}
- “=” “[]” “()” "->"必须作为成员函数重载
3.运算符重载函数作为友元函数
没有this指针
class Complex
{private:double real;double imag;public:friend Complex operator+(Complex &c1, Complex &c2); //友元函数运算符重载函数
};Complex operator+(Complex &c1, Complex &c2)
{Complex c;c.real = c1.real + c2.real;c.imag = c1.imag + c2.imag;return c;
}
- “<<” “>>” 只能用友元函数重载
4.单目运算符重载
C++规定 有一个int型形参的重载函数,是后置自增自减运算函数
class Time
{private:int minute;int sec;public:Time operator++(); //++iTime operator++(int); //i++
};Time Time::operator++() //++i
{if(++sec >= 60){sec-=60;++minute;}return *this;
}Time Time::operator++(int) //i++
{Time temp(*this); //临时对象 保存自加前的状态sec++;if(sec >= 60){sec-=60;++minute;}return temp; //返回的是自加前的对象
}
5.重载流运算符(必须是友元函数)
class Complex
{private:double real;double imag;public:friend ostream& operator<< (ostream & , Complex & );friend istream& operator>> (istream & , Complex & );
};ostream& operator<<(ostream &output, Complex& c)
{output<< "(" << c.real << "+" <<c.imag <<"i)" <<endl;return output;
}istream& operator>>(istream& input, Complex& c)
{cout<<"please input real part and imaginary part of complex number:";input >> c.real >> c.imag; return input;
}
6.转换构造函数
只有一个参数,如果有多个参数,他就不是转换构造函数
class Complex
{private:double real;double imag;public:Complex(double r){real = r; //将double型r转换为Complex类的对象imag = 0;}
}
7.类型转换函数
class Complex
{private:double real;double imag;public:operator double(){return real;}
}
- 类型转换函数函数名为operator double
- 返回值类型由函数名中指定类型名确定
- 只能作为成员函数
第11章 继承与派生
1.派生类构成
- 派生类把基类的全部成员接受过来(不包括构造函数和析构函数)
- 调整从基类接收的成员
若派生类中声明一个与基类成员同名成员,则派生类中的新成员会覆盖基类的同名成员;对于成员函数要考虑函数重载的情况。
- 派生类新增加的成员
2.权限
基类成员在基类的访问属性 | 继承方式 | 基类成员在派生类的访问属性 |
---|---|---|
public | public | public |
protected | public | protected |
private | public | 不可访问 |
public | protected | protected |
protected | protected | protected |
private | protected | 不可访问 |
public | private | private |
protected | private | private |
private | private | 不可访问 |
派生类中访问属性 | 在派生类中 | 在派生类外部 | 在下一层公用派生类 |
---|---|---|---|
public | 可以 | 可以 | 可以 |
protected | 可以 | 不可以 | 可以 |
private | 可以 | 不可以 | 不可以 |
不可访问 | 不可以 | 不可以 | 不可以 |
3. 派生类的构造函数
派生类的构造函数
- 继承过来的基类成员初始化工作由派生类构造函数负责
Class A //基类
{private:int x, y;public:A(int a, int b) //基类构造函数{x=a;y=b;}
};/* 形式1: */
class B:public A //派生类
{private:int z;public:B(int a, int b, int c):A(a, b) //派生类构造函数{z=c;}
};/* 形式2 */
class B:public A //派生类
{private:int z;public:B(int a, int b, int c); //派生类构造函数声明
};//类外定义
B::B(int a, int b, int c):A(a, b)
{z=c;
}
构造函数执行顺序:
- 1.基类构造函数(基类数据成员初始化)
- 2.子对象构造函数(子对象数据成员初始化)
- 3.派生类构造函数(派生类数据成员初始化)
派生类只需写其上一层派生类的构造函数,不必每一层都写出来
派生类构造函数特殊形式
- 当不需要对派生类新增成员操作时,派生类构造函数的函数体可为空
- 当基类中没有定义构造函数或定义了没有参数的构造函数,派生类构造函数可不写基类构造函数(系统默认调用基类默认构造函数)
- 当基类、子对象、派生类都不需要参数,派生类可以省略显式构造函数,系统调用默认的构造函数
- 当基类或子对象中定义了带参数的构造函数,则派生类必须显式的定义构造函数
4. 派生类的析构函数
析构函数执行顺序:
- 1.派生类自己的析构函数(处理新增成员)
- 2.子对象的析构函数(处理子对象)
- 3.基类的析构函数(处理继承的成员)
5.多重继承
class D: public A, public B, protected C
{...
};
多重继承构造函数调用顺序:
- 1.调用基类的构造函数,调用次序按照他们继承时说明的次序(从左往右)
- 2.调用子对象的构造函数,调用次序按照它们在类中说明的次序(从上到下)
- 3.调用派生类的构造函数
多重继承析构函数调用顺序:
与多继承构造函数调用顺序相反
6.虚基类
在继承间接共同基类时,只保留一份基类成员
class A //基类
{};
class B : virtual public A //B是A的公有派生类,A是B的虚基类
{};
class C : virtual public A //C是A的公有派生类,A是C的虚基类
{};
- 虚基类实在声明派生类时,指定继承方式时声明的
- 为保证虚拟类在派生类只继承一次,该虚基类的所有直接派生类都要声明为虚基类
虚基类的初始化
class A //基类A
{A(int i){};
};class B : virtual public A //A是B的虚基类
{B(int n): A(n){}
};class C : virtual public A //A是C的虚基类
{C(int n):A(n){}
};class D : public B, public C //正常继承
{D(int n): A(n), B(n), C(n){} //D中要对所有基类初始化;重要!也就是还要对虚基类A初始化
};
- 在最后的派生类中不仅要负责对直接基类初始化,还要负责虚基类的初始化!!!
- 编译系统只执行最后派生类的构造函数调用,忽略虚基类的其他派生类(B和C)
7.基类和派生类的转换
只有公有派生类才是基类的真正子类型,它完整的继承了基类的功能
- 1.派生类对象可以向基类对象赋值
儿子可以给爸爸赋值,反之不对
A a1; //基类A
B b1; //A的派生类B
a1 = b1; //舍弃派生类自己独有的成员,“大材小用”
- 2.派生类对象可以替代基类对象向基类对象的引用进行赋值或初始化
A a1;
B b1;
A &r = b1; //指的是b1中基类的那一部分的别名
- 3.函数参数是基类对象或基类对象引用,实参可以用子类对象
void fun(A &r){}
fun(b1); //正确 只输出派生类中的基类成员
- 4.指向基类对象的指针变量也可以指向派生类对象(重要)
指向儿子中继承的那一部分成员
A *p1;
B b;
p1 = &b; //指向B中继承A的那部分数据
8.小结
类的继承派生体现的是 “是”的关系
类的组合(子对象)体现的是“有”的关系
继承时纵向的,组合是横向的
第12章 多态性和虚函数
1.静态多态和动态多态
静态多态性 | 动态多态性 |
---|---|
通过函数重载实现,编译时多态 | 通过虚函数实现,运行时多态 |
虚函数 | 函数重载 |
---|---|
函数首部是相同的 | 函数首部是不同的(参数个数或类型) |
2.虚函数(=基类声明虚函数+基类对象指针调用派生类方法)
同一类族中不同类的对象,对同一函数调用作出不同的响应
#include <iostream>
using namespace std;class Student
{protected:int num;public:Student(int n):num(n){};virtual void display() //虚函数{cout<<num<<endl;}
};class Graduate:public Student
{private:float wage;public:Graduate(int n, float m):Student(n),wage(m){};void display() //派生类重新定义虚函数{cout<<num<<" "<<wage<<endl;}
};int main()
{Student s(1001);Graduate g(1001, 4500.5);Student *p = &s; p->display();p=&g; //父类指针指向子类/* 若display不是虚函数,那么只能调到父类的display方法;若display在基类中被声明为虚函数,那么可以通过父类指针调到子类的display函数*/p->display(); return 0;
}
1001
1001 4500.5
使用虚函数
- 类外定义虚函数不必要加上
virtual
- 基类成员函数被声明为虚函数,其派生类的同名函数自动成为虚函数,
vitual
可加可不加 - 定义基类对象指针,指向同一类族;指谁调谁(调用的是指向对象的同名函数)
- 虚函数+指向基类对象指针 = 动态多态性
3.虚析构函数
未声明为虚析构函数时,new 出来的对象,delete时只会调用基类的析构函数
#include <iostream>
using namespace std;class Point
{public:Point(){}virtual ~Point() //虚析构函数{cout<<"executing Point destructor"<<endl;}
};class Circle:public Point
{public:Circle(){}~Circle(){cout<<"executing Circle destructor"<<endl;}private:int radus;
};int main()
{Point *p = new Circle; //动态分配空间delete p;return 0;
}
executing Circle destructor
executing Point destructor
- 先调用派生类析构函数,再调用基类析构函数
- 基类析构函数声明为虚函数后,该基类的所有派生类的析构函数都声明为虚函数
- 构造函数不能被声明为虚函数!!!
4.纯虚函数
virtual float area() const = 0;
- 纯虚函数没有函数体
- 最后的"=0"不表示返回值为0
- 这是个声明语句最后要加";"
- 基类声明了纯虚函数,派生类没有对该函数定义,那么该虚函数在派生类中仍然是纯虚函数
5.抽象类
定义抽象类的目的:用它作为基类去建立派生类
- 凡是包含纯虚函数的类都是抽象类
- 包含纯虚函数的类是无法建立对象的
6.静态关联和动态关联
动态关联
- 通过基类指针调用虚函数(pt->shapeName()),在运行阶段才能确定是哪一类的虚函数。
静态关联
- 通过对象名调用虚函数(point.shapeName()),在编译阶段就能确定是哪一个类的虚函数
第13章 输入输出流
- 标准输入输出(标准I/O)
- 文件输入输出(文件I/O)
- 串I/O
1.C++流库
两个基类
- ios类(负责高层操作,面向用户)
- streambuf类(负责低层操作,面向底层)
有关头文件
- iostream:输入输出流
- fstream:文件I/O
- strstream:字符串I/O
- stdiostream:混合C和C++
- iomanip:格式化I/O用
2.标准输出流(ostream类的3个对象)
- cout
- cerr(只能输出显示器,不经过缓冲区)
- clog(存放在缓冲区,当缓冲区满了或遇到endl,向显示器输出)
3.格式控制
- 头文件
#include <iomanip>
用流对象的成员函数控制
precision(n); //实数精度为n为
width(n); //字段宽度n位
fill(c); //设置填充字符c
设置格式
setf(); //设置格式
unsetf(); //取消设置格式
参数:
iso::left
iso::dec //整数基数是10
iso::oct //整数基数是8
iso::hex //整数基数是16
-
成员函数
width(n)
和setw(n)
只对其后面第1个输出项有效 -
要重新设置格式
setf()
,先取消usetf()
-
setf()
可以设置多个setf(ios::internal | ios::showpos)
成员函数put()
专门用于输出单个字符的成员函数put
cout.put('A');
4.标准输入流(cin)
键盘输入完按Enter后输入,跳过空格、Tab键、换行符等空白字符
if(!cin)cout<<"error";
5.字符输入的成员函数
- get()读入一个字符
当遇到输入流中的文件结束符,函数返回EOF,即**-1**
#include <iostream>
using namespace std;int main
{int c;while( ( c = cin.get() )!=EOF )cout.put(c);return 0;
}
cin.get(ch); //从输入流中读取一个字符,赋给ch/* 等价 */
cin.get(字符数组, 字符个数n, 终止字符); //从输入流中读取n-1字符,赋给字符数组 n-1个有效字符和一个'\0'
cin.getline(字符数组, 字符个数n, 终止字符);
6.istream类的其他成员函数
- eof()
文件结束返回 非零
文件没结束返回 0
#include <iostream>
using namespace std;int main()
{char c;while( !cin.eof() ) //eof()为假表示未遇到文件结束符if( (c=cin.get())!=' ' ) //检查读入的字符是否为空格字符cout.put(c);return 0;
}
- peek() 观察
c = cin.peek(); //返回指针当前指向的字符
- putback()
cin.putback(ch); //将ch插在当前指针之后的位置(注意是插入,不是覆盖)
- ignore()
cin.ignore(n, 终止字符) ; //跳过n个字符或者 终止字符以前的都被忽略
#include <iostream>
using namespace std;int main()
{char ch[20];cin.ignore(18, 'A');cin.get(ch, 20, '/');cout<<ch<<endl;return 0;
}
abcA123/
123
7.文件
文件两大类:
- 程序文件
- 数据文件
根据文件中数据的组织形式分:
- ASCII文件 1个字节(8位)存放1个字符(文本文件、字符文件)
- 二进制文件(内部格式文件、字节文件)
I/O功能
- 低级I/O:以字节为单位输入输出(二进制形式)
- 高级I/O:有意义单位输出(ASCII字符)
8.文件流类和文件流对象
- ifstream:从istream派生来。支持从磁盘文件输入
- ofstream:从ostream派生来。支持向磁盘文件输出
- fstream: 从iostream派生来。支持对磁盘文件的输入输入
1.打开文件
ofstream outfile;
outfile.open("f1.dat", ios::out); //以输出方式打开一个文件
方式 | 作用 |
---|---|
ios::in | 以输入方式打开文件 |
ios::out | 以输出方式打开文件(默认方式),如果已有此名字的文件,则将其原有的内容全部清除 |
ios::app | 以输出方式打开文件,追加写 |
ios::ate | 打开已有文件,文件指针指向文件末尾 |
ios::trunc | 打开文件,若如果文件存在,则删除其中的全部数据;如果不存在,则建立新文件。ios::out方式的默认格式 |
ios::binary | 二进制格式打开文件,若没指定则默认ASCII方式打开 |
ios::nocreate | 打开一个已有文件,如文件不存在,则打开失败 |
ios::noreplace | 如果文件不存在则建立新文件,如果文件存在则操作失败 |
ios::in | ios::out | 以输入输出方式打开文件,可读可写 |
ios::out | ios::binary | 以二进制输出方式打开文件 |
ios::in | ios::binary | 以二进制输入方式打开文件 |
- 不能组合互斥的方式,如:ios::nocreate | ios::noreplace
//检测文件打开是否失败
if(outfile.open("f1.dat", ios::app) ==0)cout<<"open error";if( !outfile.open("f1.dat", ios::app) )cout<<"open error";outfile.close(); //关闭文件
9.对ASCII码文件的操作
例. 键盘输入10个整数送到数组,在将数组存到磁盘文件
#include <iostream>
#include <fstream>
using namespace std;int main()
{int a[10];ofstream outfile("d:\\f1.dat", ios::out); //输出格式打开文件 if( !outfile ) //判断是否打开成功,打开失败时返回0 {cerr<<"open error!"<<endl;exit(1); }cout<<"enter 10 numbers:"<<endl;for(int i=0; i<10; i++){cin>>a[i];outfile<<a[i]<<" ";}outfile.close(); //关闭文件 return 0;
}
10.对二进制文件的操作
与ASCII码文件不同
既能做输入又能输出的文件
istream &read(char *buffer, int len); //读
ostream &write(const char *buffer, int len); //写
#include <iostream>
#include <fstream>
using namespace std;struct Student
{char name[20];int num;int age;char sex;
};int main()
{Student stu[2] = {{"Li", 1001, 18, 'f'}, {"Wang", 1002, 17, 'f'}};//输出到磁盘文件 ofstream outfile("d:\\stu.dat", ios::binary);if(!outfile){cerr<<"open error"<<endl;abort(); //退出程序 }for(int i=0; i<3; i++)outfile.write( (char *)&stu[i], sizeof(stu[i])); //要强制转化为 char * outfile.close(); //关闭输出流 //从磁盘文件读取数据Student stu2[2];ifstream infile("d:\\stu.dat", ios::binary);if(!infile){cerr<<"open error"<<endl;abort();} for(int j=0; j<3; j++)infile.read( (char *)&stu2[j], sizeof(stu2[j]));infile.close(); //关闭输出流 //打印读出来的数据 for(int k=0; k<2; k++)cout<<stu2[k].num<<" "<<stu2[k].name<<" "<<stu2[k].age<<" "<<stu2[k].sex<<endl;return 0;
}
1001 Li 18 f
1002 Wang 17 f
11.文件指针有关函数
infile.seekg(100); //输入文件位置标记向前移动100字节位置
infile.seekg(-50, ios::cur); //输入文件中位置标记从当前位置后移50字节
outfile.seekp(-75, ios::end); //输出文件中位置标记从文件尾后移50字节
免费分享超过1000本计算机类电子书,包含编程语言、大数据、机器学习、校招面试经验等
()
更多考研资料、调剂技巧可以关注我的公众号,也可以加我的微信
C++程序设计重点总结(谭浩强版)
免费分享超过1000本计算机类电子书,包含编程语言、大数据、机器学习、校招面试经验等
()
文章目录
- 第1章 C++初步知识
- 1.流程
- 第3章 程序设计
- 1.优先级
- 2.switch
- break continue
- 第4章 函数
- 1.内置函数
- 2.函数重载
- 3.函数模板
- 4.带默认参数的函数
- 5.动态存储和静态存储
- 6.auto(自动变量、动态存储)
- 7.static(静态局部变量、静态存储)
- 8.register(寄存器变量、在内存)
- 9.extern(外部变量、静态存储、别的文件可引用)
- 10.static(静态外部变量、静态存储、只限本文件中使用)
- 11.内部函数外部函数
- 第5章 数组
- 1.一维数组
- 2.二维数组
- 3.字符数组
- 4.字符串处理函数
- 5.string
- 6.string []
- 第6章 指针和引用
- 1.指针 和 指针变量
- 2.`&` `*`
- 3.指针作为形参
- 4.指向数组
- 5.指针变量 和 指针函数
- 6.指针数组
- 7.const指针
- * void指针
- 8.指针小结
- 9.引用(本质是指针常量)
- 10.参数传递
- 第7章 用户自定义数据类型
- 1.结构体类型(`struct`)
- 2.链表
- 3.`new`和`delete`
- 4.枚举类型(枚举常量)
- 5.typedef声明新类型名
- 第9章 类和对象
- 1.构造函数
- 2.析构函数
- 3.构造函数、析构函数执行顺序
- 4.对象指针
- 5.this指针
- 6.共享数据的保护
- 7.指向对象的常指针
- 8.指向常对象的指针变量
- 9.对象的常引用
- 10.对象的动态建立和释放
- 11.1 对象赋值
- 11.2 对象复制
- 13.静态成员(static)
- 14.友元
- 友元函数
- 友元类
- 15.类模板
- 第10章 运算符重载(不在考纲内)
- 1.运算符重载规则
- 2.运算符重载函数作为类成员函数
- 3.运算符重载函数作为友元函数
- 4.单目运算符重载
- 5.重载流运算符(必须是友元函数)
- 6.转换构造函数
- 7.类型转换函数
- 第11章 继承与派生
- 1.派生类构成
- 2.权限
- 3. 派生类的构造函数
- 4. 派生类的析构函数
- 5.多重继承
- 6.虚基类
- 7.基类和派生类的转换
- 8.小结
- 第12章 多态性和虚函数
- 1.静态多态和动态多态
- 2.虚函数(=基类声明虚函数+基类对象指针调用派生类方法)
- 3.虚析构函数
- 4.纯虚函数
- 5.抽象类
- 6.静态关联和动态关联
- 第13章 输入输出流
- 1.C++流库
- 2.标准输出流(ostream类的3个对象)
- 3.格式控制
- 4.标准输入流(cin)
- 5.字符输入的成员函数
- 6.istream类的其他成员函数
- 7.文件
- 8.文件流类和文件流对象
- 9.对ASCII码文件的操作
- 10.对二进制文件的操作
- 11.文件指针有关函数
第1章 C++初步知识
1.流程
编辑 -> 编译(.cpp) -> 连接(.obj) -> 运行(.exe) -> 分析结果
第3章 程序设计
1.优先级
! > 算术运算符 > 关系运算符 > &&和|| > 赋值运算符
2.switch
switch(表达式)
{case 常量表达式1: 语句1; break;case 常量表达式2: 语句2; break;...default: 语句; break;
}
break continue
while(表达式1)
{if()continue; //结束本次循环,继续下次循环if()break; //结束全部循环
}
- 表达式可以是数值类型包括字符类型数据
- 次序不影响结果
- case表达式的值必须互不相同
第4章 函数
C++在程序进行编译时,以程序文件模块为编译单位
1.内置函数
在编译时将调用函数的代码直接嵌入到主调函数中,而不是将流程转出去
inline int max(int, int, int);inline int max(int a, int b, int c)
{...
}
- 规模小
- 使用频繁
- 不包含复杂控制语句,如循环语句和switch语句
- 建议性,而非指令性
2.函数重载
重载函数的参数格式、参数类型、参数顺序三者中必须至少有一种不同,返回值类型可同可不同
3.函数模板
函数模板只适用于函数体相同、函数的参数相同而类型不同的情况
如果参数的个数不同,则不能用函数模板。
template <typename T>
T max(T a, T b, T c)
{...
}
4.带默认参数的函数
- 实参和形参的结合是从左至右,指定默认值的参数必须放在形参列表的最右边
- 声明中给出默认值,定义时可以省略
- 一个函数不能既作为重载函数,又作为有默认参数的函数
5.动态存储和静态存储
- 动态存储:动态分配空间
- 静态存储:分配固定空间
6.auto(自动变量、动态存储)
int f(int a)
{auto int b, c = 3; //自动变量,默认为auto,可省略
}
- f函数被调用结束后,变量被销毁
- 不赋初值,则它的值是个不确定的值
7.static(静态局部变量、静态存储)
int f(int a)
{static int b = 0; //静态局部变量
}
- f函数被调用结束后,变量保留,外边不可以调用
- 不赋初值,编译时自动赋值0或空字符
8.register(寄存器变量、在内存)
int f(int a)
{register int i=1; //寄存器变量
}
- 使用频繁的可以声明为寄存器变量
- 建议性,而非强制性
9.extern(外部变量、静态存储、别的文件可引用)
1.在一个文件内声明全局变量
int main()
{extern int a, b; //全局变量的提前引用声明cout<<a<<b<<endl;
}
int a = 15, b = -8; //全局变量
2.在多文件的程序中声明外部变量
file1.cpp
int a=3, b=4;
...
file2.cpp
extern int a, b; //在编译连接成一个程序后,将file1.cpp中的a、b作用域扩展到file2.cppint main()
{cout<<a<<" "<<b<<endl;
}
10.static(静态外部变量、静态存储、只限本文件中使用)
file1.cpp
static int a = 3; //静态外部变量,只限于本文件中用
file2.cpp
extern int a; //引用不到file2.cpp中的a,因为是静态外部变量
int main()
{cout<<a<<endl;
}
11.内部函数外部函数
- 内部函数:函数只能被本文件中的其他函数调用
static int func(int a, int b)
{...
}
- 外部函数:可供其他函数调用,默认都是外部函数
file1.cpp
int max(int x, int y)
{...
}
file2.cpp
int main()
{extern int max(int, int); //声明要调用其他文件中的max函数max(1, 2);
}
第5章 数组
- 字符串结束标志符
\0
(ASCII码为0)
1.一维数组
int a[10]; //不允许对数组的大小作动态定义const int n = 5;
int a[n]; //合法,因为n是常量
2.二维数组
int a[3][4];
int a[][4] = {1, 2, 3, 4, 5, 6}//第2维的长度不能省略
3.字符数组
char a[10] = {'I', 'a', 'm', 'h', 'a', 'p', 'p', 'y'};
char c[] = "I am happy"; //正确
c = a; //错误
c = {'I', 'a', 'm', 'h', 'a', 'p', 'p', 'y'}; //错误
c[0] = 'I'; //正确char str[20];
cin>>str; //输入字符串
cout<<str; //输出字符串 遇到'\0'结束,不包括'\0'
4.字符串处理函数
#include <string.h>
或
#include <string>
strcat
:字符串连接函数
strcat()
strcpy
:字符串复制函数(覆盖)
char str1[10], str2[] = "China";
strcpy(str1, str2); //第1个实参必须是数组名,第2个实参可以是数组名,也可以是字符串常量
strcpy(str1, "China");strcpy(str1, str2, 2); //将str2中前面2个字符复制到str1中去,然后加上'\0'
strcmp
:字符串比较函数
strcmp(str1, str2); //正确
strcmp("China", "Korea"); //正确
strcmp(str1, "Beijing"); //正确
串1 == 串2,返回0
串1 > 串2,返回正数
串1 < 串2,返回负数
strlen
:字符串长度函数
char str[10] = "China";
cout<<strlen(str); //5 不包含'\0'
5.string
string不是C++本身的基本类型,而是C++标准库中声明的一个字符串类
- 需要引入头文件
#include <string>
- 定义
string s1, s2;
s1 = "Hello";
s2[0] = 'a'; //合法修改第一个字符
- 输入输出
cin>>s1;
cout<<s1;
- 运算
string s1, s2;
s1 = "Hello";
s2 = s1; //赋值运算
s2 = s1 + s2; //连接
if(s1 > s2) ...; //比较运算符
6.string []
string name[3] = {"Zhang", "Li", "Sun"};
第6章 指针和引用
1.指针 和 指针变量
- 变量的指针:变量的地址
- 指针变量:一个变量专门存放地址(指针)
2.&
*
自右向左结合
int a = 10;
int *pointer_1, *pointer_2;
pointer_1=&a;
pointer_2 = &*pointer_1; //从右向左执行,将a的地址赋给pointer_2
3.指针作为形参
调用函数时不会改变实参指针变量的值(地址),但可改变实参指针变量所指向的值
int a = 10
int *p = a;
fun(p);
int fun(int *p){} //不改变p的值,但可改变a的值
4.指向数组
启示指向的是数组的第一个元素
int a[10];
int *p;p=&a[0]; //等价于
p=a;p+1 //表示数组的下一个元素void select_sort(int array[], int n) //等价于
void select_sort(int *array, int n)
5.指针变量 和 指针函数
- 指针变量(2个括号)【是变量】
int a=1, b=1;
int max(int x, int y);int (*p)(int, int); // 是变量,可以指向函数
p=max
p(a, b) //用函数指针调用 等价于
max(a, b)
- 指针函数(1个括号)【是函数】
int *p(int x, int y) {} // 是函数,返回值为int型指针
6.指针数组
int *p[4]; //每个元素都是指针类型
相当于:
int *p0; int *p1; int *p2; int *p3;例:
char *name[] = {"peter", "jack", "smith"}; //指针数组
[]
优先级比*
高
注意:
int (*p)[4]; //指向一维数组的指针变量 用来指向二维数组
#include<iostream>
using namespace std;int main()
{int w[3][4] = {{1,2,3, 11}, {4,5,6, 12}, {7,8,9, 13}};int (*p)[4] = w; //指向一维数组的指针变量for(int i=0; i<3; i++){for(int j=0; j<4; j++)cout<<p[i][j]<<" ";cout<<endl;}return 0;
}
7.const指针
- 指向常量的指针变量(量不能变)
const int *p = &a; // 不允许通过p修改a的值
*p = 10; //非法
p = &b; //合法
- 常指针(指针不能变)
int * const p = &a; //不允许修改p的指向,但允许修改a的值;定义时要初始化
*p = 10; //合法
p = &b; //非法
- 指向常量的常指针(量不能变,指针也不能变)
1.常变量只能由 [指向常变量的指针变量] 指向它,不能用 [一般指针]
2.[指向常变量的指针变量] 也可以指向[一般变量],那么用该指针变量访问时有常变量的特征
3.[一般指针]只能指向[一般变量]
const int * const p = &a;
*p = 10; //非法
p = &b; //非法
* void指针
- 不指向任何类型 = 指向空类型 = 指向不确定的类型
可以把
非void型
的指针赋给void型
指针变量,但不能把void型
指针直接赋给非void型
,必须先进行强制转换
int a = 3;
int *p1 = &a;
char *p2 = "new";
void *p3;
p3 = (void *)p1; //将p1的值转换为void *类型,赋给p3
cout<<*p3<<endl; //p3不能执行确定类型的变量,非法
cout<<p3<<endl; //a的纯地址,但p3并不指向acout<<*(int *)p3<<endl; //把p3的值转换为(int *)型,可以指向变量a
int main()
{int a = 3;int *p1 = &a;void *p2 = p1;cout<<p2<<endl; //0x6ffe44 p2是a的纯地址,但p2并不指向a,所以*p2是非法的 cout<<&a<<endl; //0x6ffe44 a的地址 cout<<&p2<<endl; //0x6ffe38 p2指针变量自己的地址 return 0;
}
8.指针小结
定义 | 类型 | 含义 |
---|---|---|
int *p; | int * | p为指针变量,指向int型数据 |
int *p[4]; | int *[4] | p为指针数组,指向4个元素都是int型指针的数组 |
int (*p)[4]; | int (*)[4] | p为指针变量,指向4个int数据的一维数组 |
int *p (); | int * () | p为正常函数,只是函数返回值为int型指针 |
int (*p)(); | int (*)() | p为指针变量,指向函数 |
int **p; | int ** | p为指针变量,指向一个指针(该指针指向int型数据) |
int * const p | int * const | p是指针变量,常指针,指向不能变 |
const int *p int const * p | const int * | p是指针变量,指向常量,不能通过p修改值 |
const int * const p | const int * const | p是指针变量,指向不能变且也不能通过p修改值 |
void *p | void * | p是指针变量,基类型为空(void),不指向具体对象 |
9.引用(本质是指针常量)
变量的“别名”
int a;
int &b = a; /* 引用只有声明,没有定义 */int &b; /* 非法!引用声明的同时必须初始化 */int a1, a2;
int &b = a1;
int &b = a2; /* 非法!引用声明后不能变更 */int a[5];
int &b[5] = a; //非法!不能建立引用数组
int &c[5] = a[0]; //非法!不能作为数组元素的别名int a;
int &b = a;
int *p = b; //非法!不能建立指向引用的指针int a;
int &b = a;
int *p = &b; //合法,&b是a的地址
10.参数传递
- 变量名作形参(值传递)
void swap(int a, int b) {...
}void main(){int i=1, j=2;swap(i, j);
}
- 指针作形参(值传递)
void swap(int *p1, int *p2){...
}void main(){int i=1, j=2;swap(&i, &j);
}
- 引用作形参(地址传递)
void swap(int &a, int &b){...
}void main(){int i=1, j=2;swap(i, j);
}
第7章 用户自定义数据类型
1.结构体类型(struct
)
struct Student
{int num;char name[20];char sex;
} student1 = {1001, "Zhangsan", 'M'}; //最后一个分号不能少!!!Student student2 = {1002, "Wangwu", 'F'};student1 = student2; //相同结构体类型变量可相互赋值student.num = 10010; //"."是成员运算符Student stu[3] = {1001, "Jack", 'M', 1002, "Peter", 'F', 1003, "mali", 'M'}; //结构体数组Student *p = &student1; //结构体指针等价
student1.num;
(*p).num; // "." > "*"
p->num; // "->" > "++"
p->num++; //得到num后,使用完加1
++p->num; //等到num,加1后使用
2.链表
见考试大纲
3.new
和delete
int *p1 = new int(100); //整型数
int *p2 = new int[10]; //整型数组delete p1; //释放变量
delete [] p2; //释放数组
4.枚举类型(枚举常量)
enum weekday{sun=7, mon=1, tue, wed, thu, fri, sat}; //tue为2,wed为3...往后加1Weekday workday;workday = tue; //正确
workday = 2; //错误
workday = (Weekday)2; //C语言风格强制类型转化
workday = Weekday(2); //C++风格强制类型转化
5.typedef声明新类型名
typedef int INTEGER;
INTERGER I;typedef int NUM[100]; //声明NUM为整型数组类型,包含100元素
NUM n;typedef char *STRING; //声明STRING为char *类型,即字符指针类型
STRING p;typedef int (*POINTER)(); //声明POINTER为指向函数的指针,函数返回整数值
POINTER P;typedef struct
{int month;int day;int year;
} DATE; //声明DATE为结构体类型
DATE birthday;
第9章 类和对象
1.构造函数
格式:
class Student
{private:int num;int score;char name[20];public:Student(){} //无参构造函数Student(int n) //一个参数的构造函数{num = n;score = 0;strcpy(name, "123");}Student(char nam[], int n=0, int score=0) //默认参数的构造函数{num = n;score = n;strcpy(name, nam);}Student(int n, int s, char nam[]):num(n), score(s){strcpy(name, nam);} //参数初始化列表}
注意项:
- 构造函数名必须与类名同名
- 无返回值,无类型
- 自动调用,用户不能显式调用
Student s; //正确
Student s(); //错误!不能显式调用
- 系统可自动生成无参构造函数
- 数据成员是数组时,不能用参数初始化列表,要用正常函数体初始化
- 带默认参数的构造函数,默认值在声明时给出
Student(int = 1001, int = 20); //省略形参名声明合法
- 一个类只能有一个默认构造函数
//下面两同时存在是错误的!二义性
Stduent();
Student(int num=10, int score=0);Student s; //产生二义性
- [构造函数重载] 和 [带默认参数的构造函数] 不能同时存在
//产生二义性
Box();
Box(int, int);
Box(int =10, int =10, int =10);
2.析构函数
4种情况下执行析构函数:
- 函数内定义的局部对象:函数调用结束时释放前执行
- 静态局部对象:main函数结束或exit函数结束时调用
- 全局对象:main函数结束或exit函数结束时调用
- new运算符动态建立的对象:在delete运算符释放该对象之前
析构函数不是删除对象,只是在撤销对象占用的内存之前完成一些清理工作
- 析构函数没有返回值;没有函数类型;没有函数参数;不能被重载。
- 一个类只能由一个析构函数
- 编译系统会自动生成一个析构函数(do nothing)
3.构造函数、析构函数执行顺序
先构造的后析构,后构造的先析构
4.对象指针
Time *pt;
Time t1;
pt = &ti;//等价于
(*pt).hour;
pt->hour; // "->"是指针专用的
成员函数指针变量:
void (Time::*p2)();
p2=&Time::get_time;
要求三个匹配
- 函数参数的类型和参数个数
- 函数返回值的类型
- 所属的类
5.this指针
每个成员函数都包含一个特殊的指针,指向本类对象的指针,他的值是当前被调用的成员函数所在的对象的起始地址。
//等价于
(*this).height
this->height
height
优先级:"." > “*”
6.共享数据的保护
常对象
//等价
Time const t1(12, 34, 46);
const Time t1(12, 34, 46);
- 常对象必须初始化,整个对象生命周期中,数据成员的值不能改变
- 常对象只能调用常成员函数
- 常成员函数可以访问常对象中的数据成员,但不许改变他的值
- 一定要改常对象中的某个数据成员值
mutable int count; //mutable 可变数据成员
常数据成员(访问不限,不可改值)
class Time
{const int hour;
}
- 只能通过构造函数的参数初始化表初始化
- 任何其他函数都不能改变它的值
常成员函数(访问不限,不可改值)
class Time
{void get_time() const;
}
- 只能引用本类的数据成员,但不能改变值
- 不能调用另一个非const的成员函数
7.指向对象的常指针
Time t1(19 ,12, 15);
Time * const p1;
p1 = &t1;
- 指向对象的常指针,指向后,不允许改变指向
- 但可以改变对象的数据成员
- 做形参使用:保护指向在函数运行期不允许改变指向
void fun(Time * const t);
8.指向常对象的指针变量
同 指向常变量的指针变量
- 常对象只能由[指向常对象的指针变量]指向,不能由[一般指针]指向
- 使用[指向常对象的指针变量]时,有常量特征,不能改变值
- 做形参使用:保护指向的对象在执行中不改变值
void fun(const Time *t);
9.对象的常引用
void fun(const Time &t); //t所指向的变量的值不能变
10.对象的动态建立和释放
Time *t = new Time;
delete t; //在释放内存空间前,自动调用析构函数
11.1 对象赋值
Time t1, t2;
...
t1 = t2; //同属一类的两个对象可相互赋值
- 类的数据成员中不能包括动态分配的数据,否则赋值有问题
11.2 对象复制
Box::Box(const Box&b) //复制构造函数 注意参数是引用!!
{height = b.height;width =b.width;
}...Box box2(box1); //对象复制
- 复制构造函数:只有1个参数,一般是const对象引用
- 编译系统自动提供一个复制构造函数
对象复制 | 对象赋值 |
---|---|
从无到有建立新对象 | 对已存在的对象进行赋值 |
对象复制的使用场景:
- 1.建立新对象
Box box1(12, 15, 16);
Box box2(box1); //建立新对象box2
- 2.函数参数为类对象
void fun(Box b) //函数调用时的虚实结合
{...
}
- 3.函数的返回值是类的对象
Box f()
{Box box(12, 2, 4);return box; //赋值给一个“临时对象”返回给函数调用处
}int main()
{Box b = f();return 0;
}
13.静态成员(static)
- 归属于类,所有该类的对象所共有
class Box
{public:static int height; //静态数据成员staitc int volume(); //静态成员函数
}int Box::height = 10; //静态数据成员只能在类体外进行初始化int Box::volume()
{return height;
}int main()
{Box a;//静态成员a.height; //可以,通过对象引用Box::height; //也可以,通过类名引用//静态成员函数a.volume(); Box::volume();return 0;
}
静态成员
- 内存中只占一份空间,程序运行开始时分配空间
- 静态数据成员只能在类体外进行初始化
- 静态数据成员未赋初值,默认赋0
- 静态数据成员可被对象名引用,也可通过类名引用
- 静态数据成员同样受访问权限控制
静态成员函数
- 因:静态成员函数没有
this
指针 - 果:静态成员函数不能直接访问本类非静态成员
- 一定要访问非静态成员,通过
对象名.非静态成员函数
14.友元
友元函数
- 友元函数可以访问该类的私有成员
1.普通函数作为友元函数
class Time
{private:int hour; public:friend void display(Time &); //友元函数(不属于该类)
}void display(Time &t) //普通函数
{t.hour; //可以通过对象名访问私有成员
}
- 引用私有成员要用对象名.数据成员
- 没有
this
指针
2.一个类的成员函数,作为另一个类的友元函数
class Date; //提前引用声明/* 客人 */
class Time
{private:int hour;public:void display(Date &); //正常成员函数
}/* 主人 */
class Date
{private:int hour;public:friend void Time::display(Date &); //Time类中的display函数作为本类的友元函数
}void Time::display(Date &d)
{d.hour; // 别人家的私有数据成员hour; //自己家的私有数据成员
}
注意项:
- 声明友元函数时,加上该函数所属的类
- 引用别人地私有成员要通过
对象名.数据成员
友元类
//B是A的友元类,B中的所有成员函数都是A的友元函数
class B;
class A
{ public:friend class B; //B是A的友元类
}class B
{...
}
- 友元关系不能被继承
- 友元关系是单向的,且不具有传递性
15.类模板
功能相同,只是数据类型不同
template <class T>
class Compare
{public:Compare(T a, T b) //构造函数{x=a;y=b;}T max() //成员函数类内定义 { return (x>y)?x:y; }T min();private:T x, y;
}template<class T>
T Compare<T>::min() //类外定义
{ return (x>y)?x:y;}int main()
{Compare<int> f1(3, 7); Compare<double> f2(45.2, 30.1);return 0;
}
三部曲:
- 1.写个实际的类
- 2.虚拟类名替换具体类名
- 3.第一行加入
template <class 虚拟类名>
多个类型参数的情况:
template <class T1, class T2>
class Box
{...
}
类模板 | 模板类 |
---|---|
重点是模板,产生类的模子 | 重点是类,由模板产生的类 |
第10章 运算符重载(不在考纲内)
1.运算符重载规则
- 运算符重载实质是函数重载
int operator+ (int a, int b)
{return (a+b);
}
- 不能重载的5个运算符
. * :: sizeof ?:
- 重载不能改变运算符运算对象(即操作数)个数
- 重载不能改变运算符的优先级别
- 重载不能改变运算符的结合性
- 重载运算符的函数不能有默认的参数
- 重载运算符必须至少一个参数是类的对象(或是类对象的引用),不能都是C++标准类型,以防止用户修改用于标准类型数据的运算符的性质
- 用于类对象的运算符一般必须重载,除了“=” “&”
2.运算符重载函数作为类成员函数
第一个参数是this指针隐式调用的访问
class Complex
{private:double real;double imag;public:Complex operator+(Complex &c2); //成员函数运算符重载函数
};Complex Complex::operator+(Complex &c2)
{Complex c;c.real = real + c2.real;c.imag = imag + c2.imag;return c;
}
- “=” “[]” “()” "->"必须作为成员函数重载
3.运算符重载函数作为友元函数
没有this指针
class Complex
{private:double real;double imag;public:friend Complex operator+(Complex &c1, Complex &c2); //友元函数运算符重载函数
};Complex operator+(Complex &c1, Complex &c2)
{Complex c;c.real = c1.real + c2.real;c.imag = c1.imag + c2.imag;return c;
}
- “<<” “>>” 只能用友元函数重载
4.单目运算符重载
C++规定 有一个int型形参的重载函数,是后置自增自减运算函数
class Time
{private:int minute;int sec;public:Time operator++(); //++iTime operator++(int); //i++
};Time Time::operator++() //++i
{if(++sec >= 60){sec-=60;++minute;}return *this;
}Time Time::operator++(int) //i++
{Time temp(*this); //临时对象 保存自加前的状态sec++;if(sec >= 60){sec-=60;++minute;}return temp; //返回的是自加前的对象
}
5.重载流运算符(必须是友元函数)
class Complex
{private:double real;double imag;public:friend ostream& operator<< (ostream & , Complex & );friend istream& operator>> (istream & , Complex & );
};ostream& operator<<(ostream &output, Complex& c)
{output<< "(" << c.real << "+" <<c.imag <<"i)" <<endl;return output;
}istream& operator>>(istream& input, Complex& c)
{cout<<"please input real part and imaginary part of complex number:";input >> c.real >> c.imag; return input;
}
6.转换构造函数
只有一个参数,如果有多个参数,他就不是转换构造函数
class Complex
{private:double real;double imag;public:Complex(double r){real = r; //将double型r转换为Complex类的对象imag = 0;}
}
7.类型转换函数
class Complex
{private:double real;double imag;public:operator double(){return real;}
}
- 类型转换函数函数名为operator double
- 返回值类型由函数名中指定类型名确定
- 只能作为成员函数
第11章 继承与派生
1.派生类构成
- 派生类把基类的全部成员接受过来(不包括构造函数和析构函数)
- 调整从基类接收的成员
若派生类中声明一个与基类成员同名成员,则派生类中的新成员会覆盖基类的同名成员;对于成员函数要考虑函数重载的情况。
- 派生类新增加的成员
2.权限
基类成员在基类的访问属性 | 继承方式 | 基类成员在派生类的访问属性 |
---|---|---|
public | public | public |
protected | public | protected |
private | public | 不可访问 |
public | protected | protected |
protected | protected | protected |
private | protected | 不可访问 |
public | private | private |
protected | private | private |
private | private | 不可访问 |
派生类中访问属性 | 在派生类中 | 在派生类外部 | 在下一层公用派生类 |
---|---|---|---|
public | 可以 | 可以 | 可以 |
protected | 可以 | 不可以 | 可以 |
private | 可以 | 不可以 | 不可以 |
不可访问 | 不可以 | 不可以 | 不可以 |
3. 派生类的构造函数
派生类的构造函数
- 继承过来的基类成员初始化工作由派生类构造函数负责
Class A //基类
{private:int x, y;public:A(int a, int b) //基类构造函数{x=a;y=b;}
};/* 形式1: */
class B:public A //派生类
{private:int z;public:B(int a, int b, int c):A(a, b) //派生类构造函数{z=c;}
};/* 形式2 */
class B:public A //派生类
{private:int z;public:B(int a, int b, int c); //派生类构造函数声明
};//类外定义
B::B(int a, int b, int c):A(a, b)
{z=c;
}
构造函数执行顺序:
- 1.基类构造函数(基类数据成员初始化)
- 2.子对象构造函数(子对象数据成员初始化)
- 3.派生类构造函数(派生类数据成员初始化)
派生类只需写其上一层派生类的构造函数,不必每一层都写出来
派生类构造函数特殊形式
- 当不需要对派生类新增成员操作时,派生类构造函数的函数体可为空
- 当基类中没有定义构造函数或定义了没有参数的构造函数,派生类构造函数可不写基类构造函数(系统默认调用基类默认构造函数)
- 当基类、子对象、派生类都不需要参数,派生类可以省略显式构造函数,系统调用默认的构造函数
- 当基类或子对象中定义了带参数的构造函数,则派生类必须显式的定义构造函数
4. 派生类的析构函数
析构函数执行顺序:
- 1.派生类自己的析构函数(处理新增成员)
- 2.子对象的析构函数(处理子对象)
- 3.基类的析构函数(处理继承的成员)
5.多重继承
class D: public A, public B, protected C
{...
};
多重继承构造函数调用顺序:
- 1.调用基类的构造函数,调用次序按照他们继承时说明的次序(从左往右)
- 2.调用子对象的构造函数,调用次序按照它们在类中说明的次序(从上到下)
- 3.调用派生类的构造函数
多重继承析构函数调用顺序:
与多继承构造函数调用顺序相反
6.虚基类
在继承间接共同基类时,只保留一份基类成员
class A //基类
{};
class B : virtual public A //B是A的公有派生类,A是B的虚基类
{};
class C : virtual public A //C是A的公有派生类,A是C的虚基类
{};
- 虚基类实在声明派生类时,指定继承方式时声明的
- 为保证虚拟类在派生类只继承一次,该虚基类的所有直接派生类都要声明为虚基类
虚基类的初始化
class A //基类A
{A(int i){};
};class B : virtual public A //A是B的虚基类
{B(int n): A(n){}
};class C : virtual public A //A是C的虚基类
{C(int n):A(n){}
};class D : public B, public C //正常继承
{D(int n): A(n), B(n), C(n){} //D中要对所有基类初始化;重要!也就是还要对虚基类A初始化
};
- 在最后的派生类中不仅要负责对直接基类初始化,还要负责虚基类的初始化!!!
- 编译系统只执行最后派生类的构造函数调用,忽略虚基类的其他派生类(B和C)
7.基类和派生类的转换
只有公有派生类才是基类的真正子类型,它完整的继承了基类的功能
- 1.派生类对象可以向基类对象赋值
儿子可以给爸爸赋值,反之不对
A a1; //基类A
B b1; //A的派生类B
a1 = b1; //舍弃派生类自己独有的成员,“大材小用”
- 2.派生类对象可以替代基类对象向基类对象的引用进行赋值或初始化
A a1;
B b1;
A &r = b1; //指的是b1中基类的那一部分的别名
- 3.函数参数是基类对象或基类对象引用,实参可以用子类对象
void fun(A &r){}
fun(b1); //正确 只输出派生类中的基类成员
- 4.指向基类对象的指针变量也可以指向派生类对象(重要)
指向儿子中继承的那一部分成员
A *p1;
B b;
p1 = &b; //指向B中继承A的那部分数据
8.小结
类的继承派生体现的是 “是”的关系
类的组合(子对象)体现的是“有”的关系
继承时纵向的,组合是横向的
第12章 多态性和虚函数
1.静态多态和动态多态
静态多态性 | 动态多态性 |
---|---|
通过函数重载实现,编译时多态 | 通过虚函数实现,运行时多态 |
虚函数 | 函数重载 |
---|---|
函数首部是相同的 | 函数首部是不同的(参数个数或类型) |
2.虚函数(=基类声明虚函数+基类对象指针调用派生类方法)
同一类族中不同类的对象,对同一函数调用作出不同的响应
#include <iostream>
using namespace std;class Student
{protected:int num;public:Student(int n):num(n){};virtual void display() //虚函数{cout<<num<<endl;}
};class Graduate:public Student
{private:float wage;public:Graduate(int n, float m):Student(n),wage(m){};void display() //派生类重新定义虚函数{cout<<num<<" "<<wage<<endl;}
};int main()
{Student s(1001);Graduate g(1001, 4500.5);Student *p = &s; p->display();p=&g; //父类指针指向子类/* 若display不是虚函数,那么只能调到父类的display方法;若display在基类中被声明为虚函数,那么可以通过父类指针调到子类的display函数*/p->display(); return 0;
}
1001
1001 4500.5
使用虚函数
- 类外定义虚函数不必要加上
virtual
- 基类成员函数被声明为虚函数,其派生类的同名函数自动成为虚函数,
vitual
可加可不加 - 定义基类对象指针,指向同一类族;指谁调谁(调用的是指向对象的同名函数)
- 虚函数+指向基类对象指针 = 动态多态性
3.虚析构函数
未声明为虚析构函数时,new 出来的对象,delete时只会调用基类的析构函数
#include <iostream>
using namespace std;class Point
{public:Point(){}virtual ~Point() //虚析构函数{cout<<"executing Point destructor"<<endl;}
};class Circle:public Point
{public:Circle(){}~Circle(){cout<<"executing Circle destructor"<<endl;}private:int radus;
};int main()
{Point *p = new Circle; //动态分配空间delete p;return 0;
}
executing Circle destructor
executing Point destructor
- 先调用派生类析构函数,再调用基类析构函数
- 基类析构函数声明为虚函数后,该基类的所有派生类的析构函数都声明为虚函数
- 构造函数不能被声明为虚函数!!!
4.纯虚函数
virtual float area() const = 0;
- 纯虚函数没有函数体
- 最后的"=0"不表示返回值为0
- 这是个声明语句最后要加";"
- 基类声明了纯虚函数,派生类没有对该函数定义,那么该虚函数在派生类中仍然是纯虚函数
5.抽象类
定义抽象类的目的:用它作为基类去建立派生类
- 凡是包含纯虚函数的类都是抽象类
- 包含纯虚函数的类是无法建立对象的
6.静态关联和动态关联
动态关联
- 通过基类指针调用虚函数(pt->shapeName()),在运行阶段才能确定是哪一类的虚函数。
静态关联
- 通过对象名调用虚函数(point.shapeName()),在编译阶段就能确定是哪一个类的虚函数
第13章 输入输出流
- 标准输入输出(标准I/O)
- 文件输入输出(文件I/O)
- 串I/O
1.C++流库
两个基类
- ios类(负责高层操作,面向用户)
- streambuf类(负责低层操作,面向底层)
有关头文件
- iostream:输入输出流
- fstream:文件I/O
- strstream:字符串I/O
- stdiostream:混合C和C++
- iomanip:格式化I/O用
2.标准输出流(ostream类的3个对象)
- cout
- cerr(只能输出显示器,不经过缓冲区)
- clog(存放在缓冲区,当缓冲区满了或遇到endl,向显示器输出)
3.格式控制
- 头文件
#include <iomanip>
用流对象的成员函数控制
precision(n); //实数精度为n为
width(n); //字段宽度n位
fill(c); //设置填充字符c
设置格式
setf(); //设置格式
unsetf(); //取消设置格式
参数:
iso::left
iso::dec //整数基数是10
iso::oct //整数基数是8
iso::hex //整数基数是16
-
成员函数
width(n)
和setw(n)
只对其后面第1个输出项有效 -
要重新设置格式
setf()
,先取消usetf()
-
setf()
可以设置多个setf(ios::internal | ios::showpos)
成员函数put()
专门用于输出单个字符的成员函数put
cout.put('A');
4.标准输入流(cin)
键盘输入完按Enter后输入,跳过空格、Tab键、换行符等空白字符
if(!cin)cout<<"error";
5.字符输入的成员函数
- get()读入一个字符
当遇到输入流中的文件结束符,函数返回EOF,即**-1**
#include <iostream>
using namespace std;int main
{int c;while( ( c = cin.get() )!=EOF )cout.put(c);return 0;
}
cin.get(ch); //从输入流中读取一个字符,赋给ch/* 等价 */
cin.get(字符数组, 字符个数n, 终止字符); //从输入流中读取n-1字符,赋给字符数组 n-1个有效字符和一个'\0'
cin.getline(字符数组, 字符个数n, 终止字符);
6.istream类的其他成员函数
- eof()
文件结束返回 非零
文件没结束返回 0
#include <iostream>
using namespace std;int main()
{char c;while( !cin.eof() ) //eof()为假表示未遇到文件结束符if( (c=cin.get())!=' ' ) //检查读入的字符是否为空格字符cout.put(c);return 0;
}
- peek() 观察
c = cin.peek(); //返回指针当前指向的字符
- putback()
cin.putback(ch); //将ch插在当前指针之后的位置(注意是插入,不是覆盖)
- ignore()
cin.ignore(n, 终止字符) ; //跳过n个字符或者 终止字符以前的都被忽略
#include <iostream>
using namespace std;int main()
{char ch[20];cin.ignore(18, 'A');cin.get(ch, 20, '/');cout<<ch<<endl;return 0;
}
abcA123/
123
7.文件
文件两大类:
- 程序文件
- 数据文件
根据文件中数据的组织形式分:
- ASCII文件 1个字节(8位)存放1个字符(文本文件、字符文件)
- 二进制文件(内部格式文件、字节文件)
I/O功能
- 低级I/O:以字节为单位输入输出(二进制形式)
- 高级I/O:有意义单位输出(ASCII字符)
8.文件流类和文件流对象
- ifstream:从istream派生来。支持从磁盘文件输入
- ofstream:从ostream派生来。支持向磁盘文件输出
- fstream: 从iostream派生来。支持对磁盘文件的输入输入
1.打开文件
ofstream outfile;
outfile.open("f1.dat", ios::out); //以输出方式打开一个文件
方式 | 作用 |
---|---|
ios::in | 以输入方式打开文件 |
ios::out | 以输出方式打开文件(默认方式),如果已有此名字的文件,则将其原有的内容全部清除 |
ios::app | 以输出方式打开文件,追加写 |
ios::ate | 打开已有文件,文件指针指向文件末尾 |
ios::trunc | 打开文件,若如果文件存在,则删除其中的全部数据;如果不存在,则建立新文件。ios::out方式的默认格式 |
ios::binary | 二进制格式打开文件,若没指定则默认ASCII方式打开 |
ios::nocreate | 打开一个已有文件,如文件不存在,则打开失败 |
ios::noreplace | 如果文件不存在则建立新文件,如果文件存在则操作失败 |
ios::in | ios::out | 以输入输出方式打开文件,可读可写 |
ios::out | ios::binary | 以二进制输出方式打开文件 |
ios::in | ios::binary | 以二进制输入方式打开文件 |
- 不能组合互斥的方式,如:ios::nocreate | ios::noreplace
//检测文件打开是否失败
if(outfile.open("f1.dat", ios::app) ==0)cout<<"open error";if( !outfile.open("f1.dat", ios::app) )cout<<"open error";outfile.close(); //关闭文件
9.对ASCII码文件的操作
例. 键盘输入10个整数送到数组,在将数组存到磁盘文件
#include <iostream>
#include <fstream>
using namespace std;int main()
{int a[10];ofstream outfile("d:\\f1.dat", ios::out); //输出格式打开文件 if( !outfile ) //判断是否打开成功,打开失败时返回0 {cerr<<"open error!"<<endl;exit(1); }cout<<"enter 10 numbers:"<<endl;for(int i=0; i<10; i++){cin>>a[i];outfile<<a[i]<<" ";}outfile.close(); //关闭文件 return 0;
}
10.对二进制文件的操作
与ASCII码文件不同
既能做输入又能输出的文件
istream &read(char *buffer, int len); //读
ostream &write(const char *buffer, int len); //写
#include <iostream>
#include <fstream>
using namespace std;struct Student
{char name[20];int num;int age;char sex;
};int main()
{Student stu[2] = {{"Li", 1001, 18, 'f'}, {"Wang", 1002, 17, 'f'}};//输出到磁盘文件 ofstream outfile("d:\\stu.dat", ios::binary);if(!outfile){cerr<<"open error"<<endl;abort(); //退出程序 }for(int i=0; i<3; i++)outfile.write( (char *)&stu[i], sizeof(stu[i])); //要强制转化为 char * outfile.close(); //关闭输出流 //从磁盘文件读取数据Student stu2[2];ifstream infile("d:\\stu.dat", ios::binary);if(!infile){cerr<<"open error"<<endl;abort();} for(int j=0; j<3; j++)infile.read( (char *)&stu2[j], sizeof(stu2[j]));infile.close(); //关闭输出流 //打印读出来的数据 for(int k=0; k<2; k++)cout<<stu2[k].num<<" "<<stu2[k].name<<" "<<stu2[k].age<<" "<<stu2[k].sex<<endl;return 0;
}
1001 Li 18 f
1002 Wang 17 f
11.文件指针有关函数
infile.seekg(100); //输入文件位置标记向前移动100字节位置
infile.seekg(-50, ios::cur); //输入文件中位置标记从当前位置后移50字节
outfile.seekp(-75, ios::end); //输出文件中位置标记从文件尾后移50字节
免费分享超过1000本计算机类电子书,包含编程语言、大数据、机器学习、校招面试经验等
()
更多考研资料、调剂技巧可以关注我的公众号,也可以加我的微信