深入探索C++对象模型 之 构造,解构,拷贝语意学

来源:互联网 发布:js获取tr下面所有td值 编辑:程序博客网 时间:2024/04/30 22:29

纯虚拟函数的存在

一个pure virtual function 能被定义和静态地调用,不能经由虚拟机制调用。但是要不要定义又class 设计者决定。唯一例外的情况是pure virtual destructor class 设计者一定得定义它,因为一个derived class destructor 会被编译器加以扩展,以静态调用的方式调用其“每一个virtual base class”以及“上一层base class”的destructor。因此,只要缺乏任何一个base class destructors 的定义,就会导致链接失败。C++语言保证:继承体系中每一个class object destructors 都会被调用。所以编译器不能够压抑这个调用操作。一个比较好的替代方案就是,不要把virtual destructor 声明为pure

 

虚拟规格的存在

当一个函数定义的内容与类型无关时,不应该把他定义成virtual。一般而言,把所有的成员函数都声明为virtual function,然后再靠编译器的优化操作把非必要的virtual invocation 去除,并不是好的设计观念。

 

虚拟规格中const 的存在

虚拟函数最好不要声明成const ,因为你不能保证derived instance 不修改某个data member

 

“无继承”情况下的对象构造

C中的struct:

C中,全局变量如果没有明确的初始化操作,被认为是“临时性的定义”,在程序中可以发生多次,最后被链接器折叠起来,只留下单独一个实体,被放在程序 data segment 中一个“特别保留给未初始化的global objects 使用”的空间。又叫做“BSS”(Block Started by Symbol)。而CC++ 的一个差异就在于,BSS data segment C++ 中相对地不重要。C++ 的所有全局对象都被当作“初始化过的数据”来支持。当然,如果一个local object 没有先经过初始化就使用它的初值的话,可能会成为一个潜在的程序臭虫。因为没有声明constructor ,那么用new 定义的object 也不会被初始化。

抽象数据类型(我们定义了一个constructor):

这次global object 将在程序激活时才开始初始化。而local object 和用new 运算符定义的object 变量也将被初始化。

为继承做准备(我们定义了一个constructor和一个virtual function):

这次class object 多了很多负担:vptrconstructor中附加的一些用来初始化vptr的码,合成一个copy constructor 和一个copy assignment operator,而且其操作是nontrivial(有用的)。

当有以传值方式返回对象的函数存在时,提供一个copy constructor 会触发NRVNamed Return Value)优化。

 

继承体系下的对象构造

构造的顺序:

1.       所有的 virtual base class constructors 必须被调用,从左到右,从最深到最浅:

Ø         如果class 被列于member initialization list 中,那么如果有任何明确指定的参数,都应该传递过去。若没有列于list中,而class 有一个default constructor ,也应该调用之。

Ø         此外,class中的每一个virtual base class subobject 的偏移量(offset)必须在执行期可被存取。

Ø         如果class object 是最底层(most-derived)的class,其constructors 可能被调用;某些用以支持这个行为的机制必须被放进来。

2.       所有上一层的base class constructors 必须被调用,以base class 的声明顺序为顺序(与member initialization list 中的顺序没关联):

Ø         如果class 被列于member initialization list 中,那么任何明确指定的参数,都应该传递过去。

Ø         base class 没有列于member initialization list中,而它有一个default constructor (或default memberwise copy constructor),那么就调用之。

Ø         如果base class 是多重继承下的第二或后继的base class,那么this 指针必须有所调整。

3.       如果class object virtual table pointers),它(们)必须被设定初值,指向适当的virtual tables)。

4.       记录在member initialization list 中的data members 初始化操作会被放进constructor 的函数本身,并以members 的声明顺序为顺序。

5.       如果有一个member 并没有出现在member initialization list 之中,但它有一个default constructor,那么该default constructor 必须被调用。

请注意,虽然一个classdestructor virtual 的,但是如果它被包含在另一个class中,那么它的调用操作会被静态决议出来。

虚拟继承:

传统的“constructor 扩充现象”并没有用,这是因为virtual base class 的“共享性”之故:

编译器给derived class constructor增加有个bool 参数,当是false 时,就不调用virtual base classconstructor。导致只有当一个完整的class object 被定义出来时,它才会被调用;如果object 只是某个完整object subobject,它就不会被调用。

Vptr 初始化语意学:

C++ 语言规定:在一个classbase class constructor(和destructor 中,经由构造中的对象(derived class)来调用一个virtual function,其函数实体应该是在此classbase class 中有作用的那个。也就是都静态决议,不用到虚拟机制。也就是说,虚拟机制本身必须知道是否这个调用源自一个constructor 之中。而根本的解决之道是,在执行一个constructor 时,必须限制一组virtual functions 候选名单。答案是通过vptr。而vptr 的适当初始化时间是在base class constructors 调用操作之后,但是在程序员供应的码或是“member initialization list 中所列的members初始化操作”之前。因此在class constructor member initialization list 中调用该class 的一个虚拟函数是安全的,但是在一个class member initialization list 中供应参数一个base class constructor 时调用虚拟函数就是不安全的。

 

对象复制语意学

事实上,copy assignment operator 在虚拟继承情况下行为不佳,需要小心地设计和说明。因为 copy assignment operator 缺乏一个member assignment list(也就是平行与member initialization list 的东西),因此编译器没有办法压抑上一层的base class copy operators被调用,导致在虚拟继承情况下,derived class 将对virtual base class 进行多重拷贝。C++语言说:我们并灭有规定那些代表virtual base class subobjects 是否该被“隐喻定义(implicitly defined)的copy assignment operator”指派(赋值,assign)内容一次以上。因此建议尽可能不要允许一个virtual base class的拷贝操作,甚至可能的话,不要在任何virtual base class 中声明数据。

 

解构语意学

如过class 没有定义destructor,那么只有在class 内带的member object(或是class自己的base class)拥有destructor 的情况下,编译器才会自动合成出一个来。

Destructor 被调用的顺序:

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,那么它们会以其原来的构造顺序的相反顺序被调用。