最新消息: USBMI致力于为网友们分享Windows、安卓、IOS等主流手机系统相关的资讯以及评测、同时提供相关教程、应用、软件下载等服务。

C++程序设计重点总结(谭浩强版)

IT圈 admin 0浏览 0评论

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 pint * constp是指针变量,常指针,指向不能变
const int *p
int const * p
const int *p是指针变量,指向常量,不能通过p修改值
const int * const pconst int * constp是指针变量,指向不能变且也不能通过p修改值
void *pvoid *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.newdelete

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.权限

基类成员在基类的访问属性继承方式基类成员在派生类的访问属性
publicpublicpublic
protectedpublicprotected
privatepublic不可访问
publicprotectedprotected
protectedprotectedprotected
privateprotected不可访问
publicprivateprivate
protectedprivateprivate
privateprivate不可访问
派生类中访问属性在派生类中在派生类外部在下一层公用派生类
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 pint * constp是指针变量,常指针,指向不能变
const int *p
int const * p
const int *p是指针变量,指向常量,不能通过p修改值
const int * const pconst int * constp是指针变量,指向不能变且也不能通过p修改值
void *pvoid *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.newdelete

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.权限

基类成员在基类的访问属性继承方式基类成员在派生类的访问属性
publicpublicpublic
protectedpublicprotected
privatepublic不可访问
publicprotectedprotected
protectedprotectedprotected
privateprotected不可访问
publicprivateprivate
protectedprivateprivate
privateprivate不可访问
派生类中访问属性在派生类中在派生类外部在下一层公用派生类
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本计算机类电子书,包含编程语言、大数据、机器学习、校招面试经验等

()

更多考研资料、调剂技巧可以关注我的公众号,也可以加我的微信

发布评论

评论列表 (0)

  1. 暂无评论