C++ 知识快速回顾
一. 引用
1. 引用与指针的区别
- 声明引用时必须初始化,指针可以后续赋值
- 对引用取地址就是对目标向量取地址
- 引用不是一种数据类型,不占存储单元,但是系统会给指针分配内存单元
- size(引用)=目标向量的大小,但是size(指针) = 4(指针的大小)
- 引用是直接访问,指针是间接访问
2. 引用的作用
- 引用的目的主要是在函数参数传递中,解决大块数据或对象传递效率低和空间开销大的问题
- 利用引用传递的参数,能保证参数传递时不产生副本
二. const和define的区别
- 起作用的阶段:define在编译预处理阶段起作用,const在编译运行阶段起作用
- 起作用的方式:define只是简单的字符串替换,没有类型检查。const要进行类型判断
- 存储方式 :define只是进行展开,有多少地方使用就替换多少次,它定义的宏变量在内存 中有若干备份。const定义的只读变量在运行过程中只有一份备份
- 代码调试方便程度:const可以进行调试,但是define不能。因为define定义的宏变量在预编译阶段就已经替换掉了
三. 封装
- 结合对象的全部属性和行为
- 隐蔽对象的内部属性和实现细节
四. 拷贝构造函数
Point P4(P1); <=> Point P4 = P1;
- 对象空间不包含堆和全局数据区
- 当成员存在对象之外申请的资源时,为防止析构时释放的内存再次释放造成的内存访问错误,需要定义拷贝构造函数,参数为对象的引用(参数的传递也设计到对象内存的赋值,如果不是引用会造成递归的拷贝过程)
-对于深拷贝,需要把资源复制一份,而有时候资源很大,复制需要大量的时间和空间,甚至有些资源是不可复制的,这个时候可采用带标记的拷贝构造
五. static
- 类中static成员在程序中只有一份拷贝,由所有对象共享
- 类内的static数据成员必须在类外初始化 类名::变量名 = 值 (非static的const变量必须在初始化列表初始化)
- 静态成员函数中没有this指针
静态数据成员与全局变量
- 静态数据成员没有进入程序的全局名字空间,因此不存在与程序中其他全局名字冲突的可能性
- 可以实现信息隐藏,静态数据成员可以是private成员,而全局变量不能
静态成员与非静态成员
- 静态成员之间可以互相访问,包括静态成员函数访问静态数据成员和访问静态成员函数
- 静态成员函数不能访问非静态成员函数和非静态数据成员,而非静态数据成员可以任意的访问静态成员函数和静态数据成员
六. 友元
优缺点
在需要允许某些特定的非成员函数访问一个类的私有成员(及受保护成员),而同时仍阻止一般的访问的情况下,友元是可用的。
优点:
可以灵活地实现需要访问若干类的私有或受保护的成员才能完成的任务;
便于与其他不支持类概念的语言(如[C语言](http://lib.csdn.net/base/c "C语言知识库")、汇编等)进行混合编程;
通过使用友元函数重载可以更自然地使用C++语言的IO流库。
缺点:
一个类将对其非公有成员的访问权限授予其他函数或者类,会破坏该类的封装性,降低该类的可靠性和可维护性。
七. 继承
不显式声明继承方式,默认是private(私有继承)
1. 派生类的构成
(1) 接收基类成员 对基类所有成员无选择无条件接收,但不包括基类的构造函数和析构函数
(2) 改造基类成员 重写覆盖
(3) 添加新的成员
2. 继承中的访问控制
- 公有继承
保护基类成员 | 在派生类中的访问属性 |
---|---|
公有成员 public | 公有成员 public |
保护成员 protected | 保护 protected |
私有成员 private | 不可访问 |
- 私有继承
私有基类成员 | 在派生类中的访问属性 |
---|---|
公有成员 public | 私有 private |
保护成员 protected | 私有 private |
私有成员 private | 不可访问 |
- 保护继承
保护基类成员 | 在派生类中的访问属性 |
---|---|
公有成员 public | 保护 protected |
保护成员 protected | 保护 protected |
私有成员 private | 不可访问 |
3. 派生类的构造函数
(1)单继承的构造函数
先构造基类,再根据基类构造派生类
派生类名(参数总表):基类名(基类构造函数参数表)
Student(int number, char name[], char sex) : Person(name,sex)
(2)组合单继承的构造函数
先初始化基类,再初始化内嵌对象,最后初始化派生类
*Student(int number, char name[], char sex, char s_name[], char city[]) : Person(name,sex), school(s_name,city), number(number)
其中Person是Student的基类,school是内嵌School的对象,number是Student类的新成员
(3)多继承的构造函数
派生类名(参数总表):基类1名(基类1构造函数参数表),基类2名(基类2构造函数参数表),... , 基类n名(基类n构造函数参数表)
影响多重继承基类构造函数调用函数顺序的是派生类定义时的继承列表
派生类构造函数的执行顺序
- (1)调用基类的构造函数,如有多个基类则按照被继承的顺序依次调用
- (2)调用内嵌对象的构造函数,如果有多个则按照他们在类的数据成员中的先后顺序依次调用
- (3)执行派生类的构造函数体的内容
- 如果基类的构造函数或者内嵌对象没有参数或者具有默认参数,在派生类的构造函数中可以不显式列出初始化方式
4. 派生类的析构函数
先执行派生类的析构函数,再调用内嵌对象的析构函数,最后调用基类的析构函数
5. 继承中的同名成员访问
类名::成员
多重继承对同名访问造成了困难,而且使得同一成员在内存中具有多个拷贝,增加了不必要的内存开销,因为在实际使用中没有必要占用两分内存(虚基类解决这些问题)
6. 虚基类
class 派生类名:virtual [继承方式] 基类名
- 虚基类的声明是在派生类继承基类时定义的
- 将直接基类的共同基类设置为虚基类,这时从不同路径继承过来的该类成员在内存中只拥有一个副本
- 如果虚基类定义了带参数的构造函数,而且没有默认的构造函数,那么它所有派生类(包括直接派生类和间接派生类)中,都应该通过构造函数的初始化列表对虚基类进行显示初始化。也就是说间接派生类需要在构造函数中显示构造间接基类
7. 基类与派生类的转换
任何需要基类对象的地方,都可以用公有派生类的对象来代替
8. 类与类的关系
关系 | 联系 |
---|---|
组合 | has-a 整体与部分的关系 |
继承 | is-a 特殊与一般的关系 |
使用 | uses-a 类对象作为参数 |
八. 多态
多态性:在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数。如果对象类型是派生类,就调用派生类的函数;如果对象类型是基类,就调用基类的函数
- 静态多态:通过重载实现,编译的时候就知道要调用哪个函数
- 动态多态:通过虚函数实现,
1. 虚函数
虚函数的作用:允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或者基类引用来访问这个同名函数
virtual 函数类型 函数名(参数列表)
-
virtual 只能在类定义的函数原型声明中使用,不能在成员函数实现的时候使用,也不能用来限定类外的普通函数(只能定义非静态成员函数)
-
virtual 具有继承性 默认在派生类中重写函数的时候是有virtual 的
-
虚函数调用时,应通过基类的指针或者引用调用(派生类赋值给基类的对象不行)
2. 纯虚函数
纯虚函数是在声明虚函数时被"初始化"为0的函数,他的一般形式为:
virtual 函数类型 函数名(参数列表)= 0;
纯虚函数没有函数体,与空函数体的函数有着本质的区别,纯虚函数中的 = 0只起形式上的作用
- 带有纯虚函数的类是抽象类,抽象类不能实例化,但可以声明一个抽象类的指针和引用