Skip to content

Latest commit

 

History

History
57 lines (41 loc) · 4.02 KB

File metadata and controls

57 lines (41 loc) · 4.02 KB

关于对象 Object Lessons

  • 类对象大小的影响因素:非静态成员变量,支持虚函数所产生的大小,继承,因内存对齐填补上的大小。

加上封装后的布局成本 Layout Costs for Adding Encapsulation

  • 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

1.1 C++ 对象模式 The C++ Object Model

  • 对于存在虚函数的类,一定会产生一个虚函数表。这个虚函数表是属于类的,一个类只有一个虚函数表。类实例化对象中有一个指针指向这个虚函数表。
  • 只要基类有虚函数,派生类不论实现或没实现,都有一个虚函数表。虚函数表是在编译时确定的,属于类而不属于某个类的实例。
  • 基类的虚函数表和子类的虚函数表不是同一个表。如果Class B继承了Class A,其虚函数表是在Class A虚函数表的基础上有所改动的,变化的仅仅是在子类中重写的虚函数。如果子类没有重写任何虚函数,那么子类的虚函数表和父类的虚函数表在内容上是一致的。因此之所以实现了多态完全是因为子类的虚函数指针与虚函数表的内容与基类不同
  • 虚函数指针的位置不是由C++标准规定的,而是由编译器决定的。不同编译器可能采用不同的方式组织对象的内存布局,因此虚函数指针的具体位置可能有所不同。

多重继承

  • 在多重继承的情况下,有多少个基类就有多少个虚函数表指针,前提是基类要有虚函数才算上这个类。
    • 子类虚函数会覆盖每一个父类的每一个同名虚函数。例如:协变
    • 父类中没有的虚函数而子类有,填入第一个虚函数表中(即子类与第一个父类内容相同的虚函数表),且用父类指针是不能调用。
    • 父类中有的虚函数而子类没有,则不覆盖。仅子类和该父类指针能调用。

多态和缺省参数

  • 多态和缺省参数是不同的。多态是运行时动态绑定,而缺省参数是编译时静态绑定。因此,绝不重定义继承而来的缺省参数值!

1.2 关键词所带来的差异 A Keyword Distinction

  • 什么时候该使用struct替换class?当更能明确表示类型含义的时候。
    • 单纯表示数据集合体,没有private data,也没有member function的时候。
    • 只是想利用struct默认的public接口声明(或者说主张使用struct表示类中成员变量和成员函数都是public的)。

1.3 对象的差异 An Object Distinction

  • 一个指针或者引用之所以支持多态,是因为它们并不引发内存中任与类型有关的内存委托操作,会受到改变的,只有它们所指向的内存的大小和内存解释方式而已。
  • 当一个base class object被直接初始化为一个derived class object时,derived object就会被切割以塞入较小的base type内存中,derived type将没有留下任何痕迹。多态于是不再呈现。
  • 一个class所占的大小包括:
    • 非静态成员所占的大小。
    • 由于内存对齐填补上的大小。
    • 支持虚函数而产生的大小。
    • 继承。