c++对象模型笔记

时间:2023-01-02 10:45:41
很佩服侯捷译书的细致,所以总是想看一遍他翻译的书很值。读技术的书我一向没有很好的习惯做笔记,这次破例记一下重点。(书看完了,笔记还没做完)

 

深度探索c++对象模型

第一章:关于对象

 

1. c++没有什么天生的比c庞大和迟缓。加上封装后的布局成本(layout costs for adding encapsulation)以及额外存取时间主要由virtual引起

1.1 virtual function 机制,用以支持一个有效率的”执行期绑定”

1.2 virtual base class 机制,用以实现多次出现在继承体系中的base class有一个单一而被共享的实体

 

2. c++对象模型(c++ object model) 相比简单对象模型和表格驱动对象模型的优势和坏处。其主要坏处是让编译时间来替换执行时间,是可取的

2.1 每个class都有相应的virtual table来存储virtual function指针

2.2 每个class object都被隐式的添加了一个vptr指向class的virtual table,用于RTTI的type_info object也被关联进virtual table中

 

3. 关键词的差异struct和class,策略性正确的struct

       3.1 用于c的struct中的伎俩(如当struct当作数组用)未必可行,要视编译器对protected和private定义前后位置

       3.2 c struct在c++中的一个合理用途就是在传递一个class object的部分(或全部)到一个c函数中去时,使用struct组合于class可以有效的解决

 

4. 对象的差异,ADT等于abstract data type model,觉得可以称之为基于对象以区别面向对象, OO等于object-oriented module

       4.1 ADT (又称OB)和OO是很不一样的, 只有OO中virtual才能体现出威力。两者互用是危险的,使用OB来表达OO,容易造成数据被截切(sliced)

       4.2 体现OO的唯一方法是通过带有virtual function的(指针)对象。除此之外,所有的信息都可以在编译期决定而非运行期

 

5. 一个class object需要多少内存?要考虑的是一下三个方面

       5.1 其nonstatic data members的总和大小

       5.2 加上任何由于alignment的需要而填补上去的空间

       5.3 加上为了支持virtual而由内部产生的任何额外负担(overhead)

 

注:我想带有virtual function的OB对于虚函数的调用也是由编译期决定的,而非运行期。这使得inline virtual function是有效的(见书第一章最后页)。*** C++是一门相当混杂的面向对象语言。Stl类库的存在是OB的特性最好解释(除所有的stream类除外)。OO部分都要靠指针和引用来体现,但是指向的实体肯定和对象在何种内存中无关(栈、堆)。

 

 

 

 

 

 

 

第二章 构造函数语意学

 

6.编译器并不是总是合成default constructor (同理copy constructor, assignment),只有在编译器需要的时候才会合成构造函数等。Nontrivial default constructor就是编译器所需要才合成出来的函数。

       6.1 带有default constructor的member class object

       6.2 带有default constructor的base class

       6.3 带有一个virtual function的class

       6.4 带有一个virtual base class的class

不在上述4个中的而又没有声明任何constructor的class,意味着它们拥有的是implicit trivial default constructors,实际上并不会被合成出来。

 

7. copy constructor的构建操作

       7.1 default memberwise initialization, 一个良好的编译器可以为大部分class objects产生bitwise copies,因为它们有bitwise copy semantics。

       7.2 bitwise copy semantics(位逐次拷贝)是指声明展现了 default copy semantics,因而编译器不需要合成拷贝构造函数出来

 

8. 不要bitwise copy semantics的条件

       8.1 当class内含一个member object而后者的class声明了一个copy constructor时

       8.2 当class继承自一个base class而后者存在有一个copy constructor时

       8.3 当class声明了一个或多个virtual functions时

       8.4 当class派生自一个继承串链,其中有一个或多个virtual base classes时

 

9. 程序转化语义学

***在严谨的c++用词中,“定义”是指“占用内存”的行为。

通过copy constructor的优化包含了多个方便,主要在:

       9.1 明确的初始化,使得对象初始化是通过拷贝构造得来

       9.2 参数的初始化

       9.3 返回值的初始化

 

10. 编译器层面的优化以及NRV, NRV的实现有赖于copy constructor的提供。Copy constructor的应用会导致编译器多多少少会对程序代码做部分转化。

 

11.成员的初始化队伍, 必须使用member initialization list的四个理由:

       11.1 当初始化一个 reference member时

       11.2 当初始化一个const member时

       11.3 当调用一个base class的constructor,而它拥有一组参数时

       11.4 当调用一个member class的constructor,而他拥有一组参数时

member initialization list处理可能重新排序,以体现出其声明次序!。!

 

 

 

 

第三章 data 语意学

 

12. Class object的大小跟下述因素有关:

       12.1 语言本身所造成的额外负担(overload),如语言因支持virtual base class导致的额外负担

       12.2 编译器对于特殊情况所提供的优化处理,比如对于空类(没有members)实体大小为1byte,但是其子类中将毫无体现。这在类库中常被用到。

       12.3 alignment的限制,为了使bus的传输效率最高,而使processor而定的数据对齐优化

 

13. Member的绑定,global object与class member的声明次序问题伴随着c++ reference manual---对于member functions本身的解析,会直到整个class的声明都出现了才开始。然而这对于member function的argument list并不为真,,argument list中的名称还是会它们第一次遭遇时被适当地决议(resolve)完成。因此在extern和nested type names之间的非直觉绑定操作还是会发生。---防御性风格来避免此类问题,始终把nested type“声明”放在class的起始处。……………… Page 91.

 

14.Data member的布局(layout),c++ standard要求,在同一个access section(public,protected,private)中,members的排列只需满足“较晚出现的members在class object的较高的地址”,相邻member之间未必连续。

 

15.Data member的存取

       15.1 static data members,经由member selection operators(也就是‘.’运算符)对一个static data member进行存取操作只是语法上的一种便宜行事而已。Static data member解决名字冲突的方法叫name-mangling.(Page 97).

       15.2 nonstatic data member,以‘.’来析取与‘->’ 析取有无不同要视该member以及class对结构而定,‘.’对member data的析取总是高效的,当然排除操作‘.’的是引用对象。

       15.3 继承与data member,简单继承结构中存取member跟c struct并无分别,这里的重点是c++语言保证出现在derived class中的base class object有其完整原样性!

       15.4 加上多态后data member数据内存布局会变得复杂,但是存取会变量仍旧是直接和快速的

       15.5 多重继承data member数据内存布局更为复杂,但是对于data member偏移的计算还是由编译器决定,因而仍旧是高效快速的

       15.6 虚拟继承,derived class object的指针(或者引用)向一个virtual base class 数据时,对该数据的存取都需要额外的一次指针跳转,这是对数据存取的唯一一处需要跳转后才能存取的地方--------当然这和15.5都是指有进取性的编译器而言(page 128的数据显示一般编译器未必如此)。

 

16. 指向data member的指针对于class的(非class object)nonstatic data member的取地址返回的总是data member的偏移量。---其值对于各个编译器来说是不一样的

 

17. 指向member的指针的效率问题,它取决于编译器对data member offset的实现以及编译器的优化能力

 

 

第四章 function语意学

 

18. Member的各种调用方式

       18.1 nonstatic member functions,nonstatic member function至少和一般的nonmember function有相同的效率。实际上每个member function都是要内化为nonmember function:

              18.1.1 改写函数的signature(指函数原型)以安插一个额外的参数调用

              18.12 将每一个对nonstatic data member的存取改为由this指针来存取

              18.1.3 将一个member function重新改写成一个外部函数,对其名称要进行所谓的”mangling”处理,以使其成为独一无二的语汇。

       18.2 名称的特殊处理(name mangling),对于member都名称一般会加上class name,无论是nonstatic member function 还是data member。对于member function更会把参数类型也编码进去以区别重载的函数。

18.3 virtual member functions,对于一个虚拟成员函数:如,(int) ptr->normalize(),normalize为一个虚拟函数,它很可能被转化为这样的一个调用,int ret = (*this->vptr[1])(this).,也就是相关的多了一层vptr到具体函数指针的转化。 但是如果直接调用(用对象’.’操作直接调用)时会使得跟一般的nonstatic member function一样加以决议。

18.4 static member functions,此类函数是唯一一种可以绕过this指针调用的函数,绑定在某个object的static member function call仅仅是因为提高访问便宜。同时提供了一个意外好处就是可以成为一个callback函数,使得c++与c-based x windows系统结合以及成功应用于threads函数上。

 

19. virtual member functions,一个class只会有一个virtual table,table内含active virtual functions的地址,包括

       19.1 这个class所定义的函数实体,会改写(overriding)一个可能存在的base class virtual function 函数实体

       19.2 继承base class的函数实体,在derived class决定不改写virtual functions才出现的情况。

       19.3 一个pure_virtual_called函数实体,它既可以扮演pure virtual functions的空间保卫者,也可以做执行期异常处理函数

 

20. 多继承下的virtual functions,对于多重继承情况,一种可能的情况是这样的:每个base class都将有一个virtual table,this指针基于第一个base class的this。每个virtual table中有整个class的virtual functions的入口,在执行期可以根据具体的指针类型在derived object this上进行偏移。***sun编译器的处理是这样的,将多个virtual table连锁为一个,在主要表格名称上加一个offset来获取次要表格的指针。

 

21. 虚拟继承下的virtual functions,在上述的主表中加入offset to virtual base的slot。由于derived object和base object的起始部分不再像“非虚拟的单一继承”情况(见15.3)那样一致。两者之间的转化就需要调整this指针,至于在虚拟继承的情况下要消除thunks,一般而言被证明为一项高难度技术。---作者的建议:不要在一个virtual base class中声明 nonstatic data members。如果这么做你会距离复杂的深渊越走越近,终不可拔。

 

22. 函数的效能,多重继承和虚拟继承带来一些意想不到的额外操作,特别是对于由编译器生成的nontrival functions可能会在不知不觉中影响程序。

 

22. 指向member function的指针。

       22.1 对于nonstatic member functions的地址,如果该函数是nonvirtual,则得到它在内存中的真正的地址,不过它并不完全,它必须绑定到object的地址上才能调用。

       22.2 支持指向virtual member functions之指针,地址结构本来只需要一个内存地址,现在则扩展到需要一个内存地址和一个slot偏移,并使之能区分。

       22.3 在多重继承下,指向member functions的指针, 为了让指向member function的指针同时支持多重继承和虚拟继承,stroustrup设计了如下的结构体做为member function指针

Struct

{

       Int delta;        //表示this指针的offset值

       Int index;        //带有virtual table索引(不能和faddr一起有效)

       Union{

       Ptrtofunc faddr; //nonvirtual member function地址(不能和index一起有效)

       Int v_offset;   //存放virtual(或多重继承中的第二或后继的)base class的vptr位置

       };

};

这项技术也被Microsoft所优化(page 179)

 

23. Member functions之指针的效率,除了多继承和virtual继承长生this指针的偏移之外,五类指针的效率基本一致(见表page182)

 

24. Inline functions,inline只是请求而没有must-inline的含义,真正inline的展开上要了解参数和临时变量产生的影响。