《深度探索c++对象模型》学习笔记

Posted 爱橙子的OK绷

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《深度探索c++对象模型》学习笔记相关的知识,希望对你有一定的参考价值。

1、c++的布局和存取时间成本?

封装并未给c++带来任何的空间或执行期的不良后果,c++在布局和存取时间上的主要额外负担由虚拟化引起。包括:
1)virtual function机制。用以支持一个有效率的“执行期绑定”。
2)virtual base class机制。用以实现“多次出现在继承体系中的base class,有一个的单一而被共享的实例”。

2、虚拟继承

虚拟继承是多重继承中特有的概念。虚拟基类是为解决多重继承而出现的。
在虚拟继承的情况下,基类不管在继承串链中被派生多少次,永远只会存在一个实例。优势主要体现在菱形继承当中。虚拟继承与普通继承不同的是,虚拟继承可以防止出现diamond继承时,一个派生类中同时出现了两个基类的子对象。也就是说,为了保证这一点,在虚拟继承情况下,基类子对象的布局是不同于普通继承的。因此,它需要多出一个指向基类子对象的指针

class iostream:public istream,public ostream...;

class istream:virtual public ios ...;
class ostream:virtual public ios ...;


如图所示菱形继承,在iostream之中就只有虚基类ios的一个实例!!!

1)时间:继承类对象访问虚基类对象中的成员时通过间接引用完成,增加引用寻址时间。
2)空间:由于共享所以不必要在对象内存中保存多份虚基类子对象的拷贝,较之多继承节省空间。

3、c++支持多态的方法

1)经由一组隐式的转化操作。例如把一个派生类指针转化为一个指向其基类的指针(也就是基类指针操作子类对象):

shape *ps = new circle();

2)经由虚函数机制:

ps->rotate();

3)经由dynamic_cast和typeid运算符:

if(circle *pc = dynamic_cast<circle*>(ps))...

在c++中,多态表示:以一个基类指针或引用,寻址出一个派生类对象。多态的主要用途是经由一个共享的接口来影响类型的封装,共享接口以虚函数机制引发,在执行期根据对象的真正类型解析出到底是哪一个函数实例被调用。
识别一个类是否支持多态,就是看它是否有虚函数。Vtbl中的虚函数一定在编译期间获知,其函数的个数、位置和地址是固定不变的,执行期间不能增、改、删。
执行期三步完成虚函数调用:1)由vptr找到vtbl;2)定位vtbl中的slot(索引值);3)通过该索引下的值调函数。

4、C++ 编译器生成默认构造函数的四种情况

c++新手的两个误解:
1)任何类如果没有定义默认构造函数,就会被合成出来一个。
2)编译器合成出来的默认构造函数会显式设定“类内每一个数据成员的默认值”。
上述两种说法都是错误的!

C++ 编译器生成默认构造函数的四种情况:
1)类成员中有成员是类对象,并且该成员的类含有默认构造函数。那么C++编译器会给这个类也生成一个默认构造函数,用来调用其成员对象的构造函数,完成该成员的初始化构造。如果这个成员的类没有给出默认构造函数,那么C++编译器也不会生成该类的默认构造函数。
2)这个类的基类有默认构造函数。那么C++编译器也会帮你生成该派生类的默认构造函数,以调用基类的默认构造函数,完成基类的初始化。如果基类没有提供这个默认构造的函数,那么编译器也不会为派生类生成默认的构造函数(这里包括两层意思,第一,基类没有任何形式构造函数;第二,基类存在其他形式的非默认构造函数,这种类型就是编译不过的,道理很明显)。
3)类中存在虚函数(新定义或继承而得到)。那么C++编译器会为你生成默认构造函数,在编译期生成虚表和虚表指针。
4)存在虚基类(有直接虚拟基类或继承链上有虚基类)。那么C++编译器会为你生成默认构造函数,以初始化虚基类表(vbtable)。

这四种情况之外,且没有声明任何constructor的类,可以说它有无用的构造函数,但实际上它根本就不会被构建出来。

5、位逐次拷贝(bitwise copy semantics)

Default constructors 和 copy constructors 在必要的时候才由编译器产生出来。“必要”即意指当class不展现bitwise copy semantics时。

也就是,如果class中展现了位逐次拷贝,编译器就不会产生出拷贝构造函数。当没有产生拷贝构造的时候,我们的类怎么产生呢。就是通过 bitwise copy 来搞定,也就是 将源类中成员变量中的每一位都逐次拷贝到目标类中,这样我们的类就构造出来了。

类在下述四种情况下不展现位逐次拷贝(这时才会而且有必要生成拷贝构造):
1)类中含有成员类对象,并且此类对象含有默认构造函数。即:有“对象”成员,而非只有基本数据类型成员。
2)基类带有拷贝构造函数。
3)类中存在虚函数(新定义或继承而得到)。重新设置vptr,并且,如果该对象是第一个对象的话还会构造vtbl。
4)存在虚基类(有直接虚拟基类或继承链上有虚基类)
前两种情况下,都是需要生成该类的拷贝构造去调用类成员对象或基类的拷贝构造函数。
后两种情况下,需要编译器来完成虚函数表(vbtl)的初始化和虚表指针(vptr)的初始化,所以如果没有显式的定义构造函数,需要编译器构造默认的构造函数。

6、必须使用成员初始化列表的情况

1)初始化引用类型成员;
2)初始化const成员;
3)基类构造函数有参数;
4)成员类对象的构造函数有参数。

初始化顺序由声明顺序决定,而与初始化列表中的顺序无关,因此编译器会对初始化列表一一处理并有可能重新排序。

7、 继承对类的数据成员的影响

1、 简单无多态的继承。不会增加内存空间以及存取上的额外负担。

2、加上多态(有虚函数)后,会有额外时间空间开销:
1)生成一个和类有关的虚表,用来存放声明的每一个虚函数地址。表中元素个数一般而言是被声明的虚函数个数,再加上一个或两个slots用以支持运行时类型识别(RTTI)。
2)在每一个类对象导入一个虚表指针,提供执行期链接,是每一个对象找到相应的虚表。
3)加强构造函数,使它能够为虚表指针设定初值,指向类的虚表。
4)加强析构函数,以处理vptr。

8、多重继承的数据布局



继承关系如下:

那么相应的数据布局为:

9、Static成员函数特性:

1)没有this指针
2)不能直接存取class中的非静态成员;
3)不能被声明为const、volatile或virtual;
4)可以不用对象访问。
Static成员函数,由于没有this,所以可以作为callback函数的候选,或者作为线程的主函数。

10、继承体系下带有数据成员的类的构造过程

构造函数的调用顺序:从root到leaf,从left到right
1)虚基类的构造函数,从左到右,从最顶层到最底层。它和非虚基类不同:是由最底层子类调用的。
2)非虚基类的构造函数,按照基类被声明顺序从左到右调用。它与虚基类的不同:是由直接子类调用的。
3)如果类中有虚表指针,则设置vptr初值;若增加有新的虚函数或者覆盖基类虚函数,则修改vtbl内的信息。
4)成员变量以其声明顺序进行初始化构造。
5)构造函数中,用户自定义的代码(user code)最后被执行。

以上是关于《深度探索c++对象模型》学习笔记的主要内容,如果未能解决你的问题,请参考以下文章

深度探索C++对象模型笔记

深度探索C++对象模型——Function语意学

深度探索C++对象模型——Function语意学

深度探索C++对象模型

《深度探索C++对象模型》第1章重点:关于对象

《深度探索c++对象模型》读书总结