0%

深度探索C++对象模型之构造、析构、拷贝语意学

5章:构造、析构、拷贝语意学

  • 可以定义和调用一个纯虚函数,不过只能被静态的调用(通过类作用域运算符),不能通过虚拟机制调用。

  • 你声明了一个纯虚析构函数,就必须定义它。因为在你提供了声明前提下,每一个derived class destructor会被编译器加以扩展,以静态调用的方式调用其“每一个virtual base class”以及“上一层base class”的destructor。因此,只要缺乏任何一个base class destructor的定义,就会导致链接失败

    读到这儿,你可能会有疑问,对于普通的类(没有虚机制参与进来,派生类的析构函数中也会逐一调用基类的析构函数)我们也没有提供它析构函数的,那为什么不会导致链接失败呢?这是因为你没有写析构函数,编译器会默默给你提供一个,以便于在后面的派生类的析构函数中调用它。这里的重点是你没有提供,也就是说你没有声明,如果你声明了,但没有提供定义,同样会导致链接失败。

  • 对于基类该不该将虚函数定义为const的,作者不建议。因为derived class中可能会修改自己的数据成员。

5.1 无继承情况下的对象构造

当编译器遇到这样的定义:

1
Point global;

C之中,global被视为一个“临时性的定义”,因为它没有明确的初始化操作。一个“临时性的定义”可以在程序中发生多次。那些实例会被链接器折叠起来,只留下单独一个实体,被放在程序data segment中一个“特别保留给未初始化之global objects使用”的空间。由于历史的缘故,这块空间被称为BSS,是Block Started by Symbol的缩写。

globalC++中被视为完全定义(它会阻止第二个或更多个定义)。CC++的一个差异就在于,BSS data segmentC++中相对地不重要。C++的所有全局对象都被当作“初始化过的数据”来对待。

为继承做准备

虚函数的引入不仅仅是每个类对象增加了一个vptr,而且会引发编译器对类产生膨胀作用。我们所定义的构造函数,编译器会附加一些代码,以便vptr初始化。合成一个copy constructor和一个copy assignment operator,它们都不是trival。因为,如果point类对象被初始化或以一个派生类对象赋值,bitwise操作就存在问题了,vptr设置会出错。

C++编译器要求编译器尽量延迟nontrivial members的实际合成操作,直到遇到使用场合为止。如果在你的设计中存在很多以传值的方式返回局部类对象,提供一个拷贝构造函数就比较合理,因为这会触发编译器的NRV优化。

5.2 继承体系下的对象构造

constructor可能内带大量的隐藏码,因为编译器会扩充每一个constructor,扩充程度视class T的继承体系而定。一般而言编译器所做的扩充操作大约如下:

  1. 记录在member initialization list中的data members初始化操作会被放进constrector的函数本身,并以members的声明顺序为顺序。
    • 如果有数据成员为类对象,并被列于member initialization list中,那么任何明确指定的参数都应该传递过去。
  2. 如果有一个member并没有出现在member initialization list之中,但它有一个default constructor,那么该default constructor必须被调用。
  3. 在那之前,如果class objectvptr,它(们)必须被设定初值,指向适当的virtual table(s)
  4. 在那之前,所有上一层的base class constructors必须被调用,以base class的声明顺序为序:
    • 如果basc class被列于member initialization list中,那么任何明确指定的参数都应该传递过去。
    • 如果base class没有被列于member initialization list中,而它有default constructor(或default memberwise copy constructor),那么就调用之。
    • 如果base class是多重继承下的第二或后继的base class,那么this指针必须有所调整。
  5. 在那之前,所有virtual base class constructors必须被调用,从左到右,从最深到最浅:
    • 如果class被列于member initialization list中,那么如果有任何明确指定的参数,都应该传递过去。若没有列于list之中,而class有一个default constructor,也应该调用之。
    • 此外,class中的每一个virtual base class subobject的偏移量必须在执行期可被存取。
    • 如果class object是最底层(most-derived)的class,其constructors可能被调用;某些用以支持这个行为的机制必须被放进来。
说明

书中接下来的章节对上面这些做了详细的叙述。全是重点,就不做总结了,自行看书

6章:执行期语意学

很重要,不做总结了,自行看书