- 类对象大小的影响因素:非静态成员变量,支持虚函数所产生的大小,继承,因内存对齐填补上的大小。
-
Zero-cost abstraction
最关键的地方是Don't pay for things you don't use
。它在于可以为你提供代码的抽象,而不加额外的负担。 -
C++
被称为Zero-cost abstraction
,某种程度上,这代表着你可以定义class
,也可以把class
转化分离为你熟悉的struct
与一系列算法。而定义class
这样的抽象,与你手动这样写相比,不会增加额外的精力与开销:struct Point3D { float x; float y; float z; } float x, y, z;
- 当然
Point3D
是一个POD(Plain Old Data)
。实际实现上更多是包含继承,甚至泛型等的多层级的类,但这些也并不增加运行时成本。
- 当然
-
真正增加空间和时间等成本的是由
virtual
引起的:Virtual function
:虚函数,支持高效的运行期多态。Virtual base class
:虚继承。例如被菱形方式继承的base class
。
- 对于存在虚函数的类,一定会产生一个虚函数表。这个虚函数表是属于类的,一个类只有一个虚函数表。类实例化对象中有一个指针指向这个虚函数表。
- 只要基类有虚函数,派生类不论实现或没实现,都有一个虚函数表。虚函数表是在编译时确定的,属于类而不属于某个类的实例。
- 基类的虚函数表和子类的虚函数表不是同一个表。如果
Class B
继承了Class A
,其虚函数表是在Class A
虚函数表的基础上有所改动的,变化的仅仅是在子类中重写的虚函数。如果子类没有重写任何虚函数,那么子类的虚函数表和父类的虚函数表在内容上是一致的。因此之所以实现了多态完全是因为子类的虚函数指针与虚函数表的内容与基类不同。 - 虚函数指针的位置不是由C++标准规定的,而是由编译器决定的。不同编译器可能采用不同的方式组织对象的内存布局,因此虚函数指针的具体位置可能有所不同。
- 在多重继承的情况下,有多少个基类就有多少个虚函数表指针,前提是基类要有虚函数才算上这个类。
- 子类虚函数会覆盖每一个父类的每一个同名虚函数。例如:协变。
- 父类中没有的虚函数而子类有,填入第一个虚函数表中(即子类与第一个父类内容相同的虚函数表),且用父类指针是不能调用。
- 父类中有的虚函数而子类没有,则不覆盖。仅子类和该父类指针能调用。
- 多态和缺省参数是不同的。多态是运行时动态绑定,而缺省参数是编译时静态绑定。因此,绝不重定义继承而来的缺省参数值!
- 什么时候该使用
struct
替换class
?当更能明确表示类型含义的时候。- 单纯表示数据集合体,没有
private data
,也没有member function
的时候。 - 只是想利用
struct
默认的public
接口声明(或者说主张使用struct
表示类中成员变量和成员函数都是public
的)。
- 单纯表示数据集合体,没有
- 一个指针或者引用之所以支持多态,是因为它们并不引发内存中任与类型有关的内存委托操作,会受到改变的,只有它们所指向的内存的大小和内存解释方式而已。
- 当一个
base class object
被直接初始化为一个derived class object
时,derived object
就会被切割以塞入较小的base type
内存中,derived type
将没有留下任何痕迹。多态于是不再呈现。 - 一个
class
所占的大小包括:- 非静态成员所占的大小。
- 由于内存对齐填补上的大小。
- 支持虚函数而产生的大小。
- 继承。