C++类中的函数不会占用类本身的内存,而是类的声明的非内联函数只会诞生一个函数体。C++在布局和存取时间上主要的额外负担由virtual
引起:
- virtual function机制 执行期动态绑定
- virtual base class机制 多次继承中,单一的基类被共享
C++的基本面向对象模型:
- 所有的非静态数据成员存放到
class
内部 - 静态成员和函数存放到
class
外部
虚函数的支持步骤:
- 每个
class
产生一些指向virtual function
的指针,存放在一个表格中,称为虚表,virtual table
(vtbl
) - 每个
class
添加一个指针,指向相关的vtbl
,称为vptr
该方式的好处是空间和存取时间效率高,缺点在于如果程序代码没变,但是class
的非静态成员改变了,那么代码就要重新编译。
虚拟继承情况下,不管发生多少次继承,永远只有一个基类。可以理解为一个bptr,有点在于减少二义性,缺点在于时空表现的效率低。
C++支持多态的方式,仅仅使用下列方法是安全的:
-
使用指针的操作,子类的指针可以指向基类:
shape *ps = new circle(); // circle是shape的子类
-
使用
virtual function
机制:ps->rotate(); // 调用实际的函数
-
使用
dynamic_cast
和typeid
运算符:if (circle *pc = dynamic_cast<circle*>(ps) ) ...
多态最主要的用法是使用共同的接口进行封装,一般来说这个接口定义在一个抽象的基类中。这就像Java的interface
关键字,不过C++没有接口的具体关键字,实际中可以使用多重继承虚基类来完成,在运行期进行检查到底是执行哪一个子类的函数。
动态绑定操作会在传入引用和指针的时候发生。
一个class object
占用内存的大小取决于下面三个:
- 非静态成员总和的大小
- 需要
alignment
的需求而padding
的空间 - 为了支持
virtual
而产生的空间,主要是各种虚表。对于指针(不包含智能指针)来说,它们占用的内存大小是固定的。
如果把子类转换成基类,那么子类会被切割,只保留基类的部分。
只有下面4种情况下,才会有自动生成的默认构造函数:
- 如果一个
class
没有构造函数,而它的成员含有默认构造函数,此时会产生一个构造函数,不过合成操作只有真正需要调用时才会发生。 - 基类由默认构造函数,派生类没有定义构造函数,此时会合成一个默认构造函数,而且会调用基类的构造函数,根据声明的次序执行
- 带有
virtual function
的class
。 当一个class
声明(或继承)一个virtual function
;class
派生自一个继承串链,其中有一个或者多个虚基类 - 带有虚基类的类
必须使用参数初始化列表的情况:
- 初始化引用对象
- 初始化
const
对象 - 调用基类的构造函数,且构造函数拥有一组参数
- 调用类成员的构造函数,而且有参数
初始化列表的顺序不一定按照声明的顺序执行,这点要格外注意!!!一般来说,很多时候初始化列表的效率更高,因此在意义明确的情况下,尽量使用初始化列表!!!