C++中的对象模型
对象模型
1 对象布局总览¶
2 继承下的对象布局(非多态)¶
继承时,基类在低地址,子类在高地址。
2.1 单继承¶
2.2 多继承¶
3 继承下的对象布局(多态)¶
- c++规定,当一个成员函数被声明为虚函数后,其派生类中的同名函数都自动成为虚函数。所以虚函数在子类中重写时可以不用加virtual关键字,但习惯上每一层声明函数时都加virtual,使程序更加清晰。
- 虚函数声明和定义分离时,声明加virtual关键字,定义不加。
3.1 单继承¶
-
有两个析构函数,但是我们其实只定义了一个析构函数(剩下的那个是编译器合成的,如下图中的红色和橙色的析构)
-
当基类的指针指向子类(Point2D * pt= Point3D();),实际上通过虚函数表调用函数时,调用的是子类的函数。这就是多态。
-
假设Point2D有一个虚函数vfunc(),但是Point3D没有重写,Point3D也会把vfunc()继承下来,因此通过子类指针调用vfunc时会调用到父类的vfun。
-
Point3D的虚析构函数(不声明时会自动生成)会自动掉用Point2D的虚析构函数。
4 构造函数语义学¶
4.1 合成默认构造函数的四种情况¶
编译器会合成默认构造函数的4种情况(总结来说只要类内有需要初始化的成分,比如虚函数表、非静态成员默认初始化、主动初始化成员,那么就会合成默认构造函数):
只要下列四种情况都不满足,就不会合成默认构造函数。
-
类中有虚函数
-
类中有等号或者大括号初始化
-
基类有自定义(或者编译器合成的有意义)的构造函数。编译器合成的有意义构造函数指1. 2. 情况
-
非静态数据成员有自定义(或者编译器合成的有意义)的构造函数。编译器合成的有意义构造函数指1. 2. 情况 基本数据成员,比如int、double、float,没有默认构造函数。
4.2 基类和数据成员的初始化时机¶
-
对于基类,用户自定义的构造函数,编译器会做一定的补充,比如自动调用父类的默认构造函数。
-
初始化列表更加高效,直接会调用string的构造函数,而不用调用string的默认构造函数再赋值。
-
初始化列表的补充,初始化列表的顺序不重要,还是按照声明的顺序初始化(由低地址向高地址构造)。
4.3 拷贝构造¶
下列四种情况使类没有位逐次拷贝的特性,会合成拷贝构造。
- data member有拷贝构造
- 基类有拷贝构造
- 类有虚函数
- 继承自虚类
5 析构函数语义学¶
如果没有自定义析构函数,如果内部的成员变量或者是基类有析构函数,那么会自动合成析构函数。 下列中①、②、③、④都不会合成析构函数。但是④是需要程序员定义析构函数的,不然会内存泄漏。
对于继承,先调用子类的析构函数,再调用父类的析构函数(会自动合成到子类的析构函数中)
析构顺序与声明顺序相反(由高地址向低地址析构):
构造由低地址向高地址构造,析构由高地址向低地址析构。 继承时,基类在低地址,子类在高地址。 原因:依赖关系