构造、析构、拷贝、语意学
纯虚函数的存在
可以定义和调用一个pure virtual function,不过只能被地调用。
pure virtual destructor必须定义它,因为每一个derived class destructor会被编译器扩张,以静态方式调用其“每一个virtual base class”以及“上一层base class”的destructor。假如缺乏了任意一个base class destructor的定义都会导致链接失败
一般的,不要把virtual destructor声明为pure
虚拟规格的存在(Presence of a Virtual Specification)
一般的,把所有成员函数都声明为virtual function,然后再靠编译器的优化操作把非必要的virtual invocation去除,并不是好的设计理念
虚拟规格中const的存在
不把virtual function声明为const,意味着函数不能够获得const reference或const pointer。声明为const却有可能遇到实际上derived instance必须修改某一个data member
其中的一个解决方法是:不再用const
重新考虑class的声明
5.1“无继承”情况下的对象构造
对于POD(plain old data)类型, 定义一个对象时编译器不会调用其构造函数, 复制时也不会调用复制构造函数(或者是没定义), 而是像C语言那样的按位复制
在C中,global被视为“临时性的定义”,因为它没有显式初始化。可以在程序中发生多次但只保留一个实例,放入BSS。BSS(Block Started by Symbol)段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。
但在C++中不支持“临时性的定义”,global在C++中被视为完全定义(它会阻止第二个或更多定义)。
抽象数据类型
explicit initialization list的缺点:
1.只有当class member都是public,此法才奏效
2.只能指定常量,因为它们在编译期就可以被评估求值
3.由于编译器并没有自动施行之,所以初始化的失败可能性会高一些
为继承做准备
virtual functions的导入促使每个class object拥有一个virtual table pointer,给我们提供了virtual接口的弹性时也额外增加了一个word的空间,但它是否有价值只有在实现完成之后才能评估是否避免
virtual functions的导入也引发编译器对于我们的class 产生膨胀作用:
1.所定义的constructor被附加了一些代码,以便将vptr初始化(base class constructor的调用之后,使用者供应代码之前)
2.合成一个copy constructor和一个copy assignment operator,且不再是trivial(但implicit destructor仍是trivial),不再bitwise
如果设计中有许多函数都需要以传值方式传回一个local class object,那么提供一个copy constructor就比较合理,能够触发NRV优化
5.2继承体系下的对象构造
constructor的调用真正伴随了什么?
1.所有virtual base class constructor必须被调用,从左到右,从最深到最浅:
如果class被列于member initialization list中,那么任何明确指定的参数都必须传递过去,否则如果class有一个default constructor,也应该调用它;
class中的每一个virtual base class subobject的偏移量offset必须在执行期可被存取;
如果class object是最底层(most-derived)的class,其constructors可能被调用,某些用以支持这个行为的机制必须被放进来
2.以base class的声明次序调用上一层base class constructors:
如果base class被列于member initialization list中,那么任何明确指定的参数都必须传递过去,否则若它有default constructor或default memberwise copy constructor,那么就调用它;
如果base class是多重继承下的第二或后继的base class,那么this指针必须有所调整。
3.如果class object有virtual table pointer(s),它(们)必须被设定初值,指向适当的virtual table(s)。
4.如果有一个member没有出现在member initialization list中,但它有default constructor,调用之。
5.将member initialization list中的data members的初始化操作以members的声明次序放进constructor的函数本身。
虚拟继承
只有当一个完整的class object被定义出来,“virtual base class constructors”才会被调用;如果object只是某个完整object的subobject,它就不会被调用
vptr初始化语意学
在一个class的constructor(和destructor)中,经由构造中的对象来调用一个virtual function,其实例应该是在此class中有作用的那个。这是因为constructor的调用顺序是由根源到末端、由内而外,当base classconstructor执行时,derived实例还没有被构造,此时还不是一个完整的对象。也因此在constructor(或destructor)中每一个调用操作以静态方式决议之
如果要在执行一个constructor时,必须限制一组virtual functions候选名单则vptr初始化操作应该在base class constructor调用操作之后,但是在程序员供应的代码或"member initialization list中所列members初始化操作“之前
constructor执行算法通常如下:
1.在derived class constructor中,”所有virtual base classes“及”上一层base class“的constructor会被调用
2.对象的vptr(s)被初始化,指向相关的virtual table(s)
3.如果有member initialization list的话此时将在constructor体内扩展开
4.最后执行程序员所提供的代码
5.3对象复制语意学
当我们设计一个class,并以一个class object指定给另一个class object时的三种选择:
1.什么都不做,因此得以实施默认行为(要支持的只是一个简单的拷贝操作,那么默认行为不但足够而且有效率)
2.提供一个explicit copy assignment operator
3.显式地拒绝把一个class object指定给另一个class object(private或delete)
一个默认的copy assignment operator不会表现出bitwise copy语意:
1.当一个class内含的member object其class有一个copy assignment operator或它的base class有一个copy assignment operator
2.当一个class声明了任何virtual functions
3.当class继承自一个virtual base class
C++ Standard上说copy assignment operator并不表示bitwise copy semantics是nontrivial。实际上,只有nontrivial才会被合成出来。
如果仅仅是为了把NRV优化开关打开而提供一个copy constructor,那么就没有必要一定要提供一个copy assignment operator。(这和三五法则相违背,可能是版本的不同导致的)
不要在任何virtual base class中声明数据
5.4对象的效能
Plain OI' Data、或者是在其基础上加上数据封装以及inline函数的使用、或者是单一继承,多重继承表现出来的都是bitwise copy语意
当引入虚拟继承时不再允许class拥有bitwise copy语意,合成拷贝构造函数和合成的拷贝赋值运算符于是被产生出来,这导致了效率成本上的一个重大增加
5.5析构语意学
如果class没有定义destructor,那么只有class内含member object(亦或是class自己的1base class)拥有destructor编译期才会自动合成出一个来,否则destructor被视为不需要,也就不需要被合成。(不管是拥有一个virtual function还是虚拟派生都是同理)
destructor被扩展流程类似constructor被扩展的流程,但顺序刚好相反:
1.destructor的函数本体首先被执行;
2.如果class拥有member class objects,而后者拥有destructors,那么它们将以声明的相反顺序而调用;
3.如果object内带一个vptr,则现在被重新设定以指向适当base class之virtual table;
4.如果有任何直接的nonvirtual base classes拥有destructor,它们将以声明的相反顺序而调用;
5.如果有任何virtual base classes拥有destructor,而前面讨论的这个class是most-derived class,那么它们会以原先构造顺序的相反顺序被调用。