深度探索C++对象模型 个人总结 第五章 构造、析构、拷贝、语意学

2021-03-08 17:27

阅读:517

标签:构造顺序   des   选择   data   this   virt   多重   全局变量   exp   

构造、析构、拷贝、语意学

纯虚函数的存在

可以定义和调用一个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 constructordefault memberwise copy constructor,那么就调用它;

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

3.如果class objectvirtual 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 classvirtual table

  4.如果有任何直接的nonvirtual base classes拥有destructor,它们将以声明的相反顺序而调用;

  5.如果有任何virtual base classes拥有destructor,而前面讨论的这个classmost-derived class,那么它们会以原先构造顺序的相反顺序被调用。

 

深度探索C++对象模型 个人总结 第五章 构造、析构、拷贝、语意学

标签:构造顺序   des   选择   data   this   virt   多重   全局变量   exp   

原文地址:https://www.cnblogs.com/GodZhuan/p/14194643.html


评论


亲,登录后才可以留言!