读书笔记 《深度探索c++对象模型》 (4)

第五章:构造,解构,拷贝语意学
5.2 继承体系下的对象构造
a) 虚拟继承

如同下面的继承情况:#include <stdio.h> #include <stdlib.h> using namespace std; class Point { public: Point() { printf("Point : constructor/n"); } virtual ~Point() { printf("Point : destructor/n"); } protected: float _x, _y; }; class Point3d : public virtual Point { public: Point3d() { printf("Point3d : constructor/n"); } ~Point3d() { printf("Point3d : destructor/n"); } protected: float _z; }; class Vertex : public virtual Point { public: Vertex() { printf("Vertex : constructor/n"); } ~Vertex() { printf("Vertex : destructor/n"); } protected: float _w; }; class Vertex3d : public Point3d, public Vertex { public: Vertex3d() { printf("Vertex3d : constructor/n"); } ~Vertex3d() { printf("Vertex3d : destructor/n"); } protected: float _u; }; int main () { Vertex3d *pV3d = new Vertex3d; delete pV3d; pV3d = NULL; return 0; } 在这种情况下,Vertex3d压制了它的两个父类对Point constructor的调用,这个调用工作由Vertext3d本身来完成。所以输出为:读书笔记 《深度探索c++对象模型》 (4)_第1张图片
b) vptr初始化语意学
接着上面的例子:#include <stdio.h> #include <stdlib.h> using namespace std; class Point { public: Point() { self(); } virtual ~Point() { printf("Point : destructor/n"); } virtual void self() { printf("Point : self/n"); } protected: float _x; }; class Point3d : public virtual Point { public: Point3d() { self(); } ~Point3d() { printf("Point3d : destructor/n"); } void self() { printf("Point3d : self/n"); } protected: //float _z; }; class Vertex : public virtual Point { public: Vertex() { self(); } ~Vertex() { printf("Vertex : destructor/n"); } void self() { printf("Vertex : self/n"); } protected: //float _w; }; class Vertex3d : public Point3d, public Vertex { public: Vertex3d() { self(); } ~Vertex3d() { printf("Vertex3d : destructor/n"); } void self() { printf("Vertex3d : self/n"); } protected: //float _u; }; int main () { Vertex3d *pV3d = new Vertex3d; delete pV3d; pV3d = NULL; return 0; } 输出为:读书笔记 《深度探索c++对象模型》 (4)_第2张图片这说明,虽然new出来的是一个Vertex3d对象,但是在每个constructor中调用的self,既不是基类的self,也不是Vertex3d的self,而都会被决议为是当前class的self。这个和vptr的初始化有关,因为vptr的初始化,是在base class constructor调用操作之后,在当前constructor执行或者当前constructor的member initialization list初始化之前。
一个constructor的执行算法如下:
1. 执行所有virtual base class以及上一层base class的constructor。
2. 初始化vptr。
3. member initialization list执行。
4. 执行constructor的代码。


5.3 对象复制语意学
如果我们明确的想要拒绝把一个class object指定给另一个class object,那么我们可以将copy assignment operator指定为private,并且不提供它的定义。
一个class对于默认的copy assignment operator,在以下四种情况下不会出现bitwise copy语意:
1. 当class内带一个class object,其class带有一个copy assignment operator;
2. 当一个class的base class有一个copy assignment operator时;
3. 当一个class声明了任何的virtual functions时;(因此不必担心vptr是否会被copy)
4. 当class继承自一个virtual base class时;


5.5 解构语意学
一个destructor的执行算法与constructor类似,但顺序相反:
1. destructor函数本身首先被执行;
2. 如果class member带有destructor,那么它们会以声明顺序的相反顺序被调用;
3. 如果一个object带有一个vptr,那么vptr被重新设定,指向适当的base class的virtual table;
4. 如果有直接的nonvirtual base class拥有destructor,那么它们会以声明顺序的相反顺序执行;
5. 如果有任何的virtual base class拥有destructor,而当前讨论的这个class是最尾端的class(保证virtua base class的destructor只被调用一次),那么它们会以其原来的构造顺序的相反顺序执行。


附:#include <stdio.h> #include <stdlib.h> using namespace std; class Point { public: Point() { } virtual ~Point() { } protected: float _x; }; class Point3d : public virtual Point { public: Point3d() { } ~Point3d() { } public: //int _z; }; class Vertex : public virtual Point { public: Vertex() { } ~Vertex() { } protected: //float _w; }; class Vertex3d : public Point3d, public Vertex { public: Vertex3d() { } ~Vertex3d() { } protected: //float _u; }; int main () { Vertex3d *pV3d = new Vertex3d; printf("%d/n", sizeof(Point)); printf("%d/n", sizeof(Point3d)); printf("%d/n", sizeof(Vertex)); printf("%d/n", sizeof(Vertex3d)); delete pV3d; pV3d = NULL; return 0; }
输出的结果可以分析为:
class Point有一个4bytes的float member和一个4bytes的vptr,该vptr中含有virtual destructor的地址;
class Point3d有一个4bytes的float member和两个4bytes的vptr,其中一个是继承下来的,另外一个含有virtua base class的offset;
class Vertex同上;
class Vertex3d有一个8bytes的Point,还分别有两个4bytes的Point3d和Vertex;
但是,如果去掉Point中的member:#include <stdio.h> #include <stdlib.h> using namespace std; class Point { public: Point() { } virtual ~Point() { } protected: //float _x; //去掉 }; class Point3d : public virtual Point { public: Point3d() { } ~Point3d() { } public: //int _z; }; class Vertex : public virtual Point { public: Vertex() { } ~Vertex() { } protected: //float _w; }; class Vertex3d : public Point3d, public Vertex { public: Vertex3d() { } ~Vertex3d() { } protected: //float _u; }; int main () { Vertex3d *pV3d = new Vertex3d; printf("%d/n", sizeof(Point)); printf("%d/n", sizeof(Point3d)); printf("%d/n", sizeof(Vertex)); printf("%d/n", sizeof(Vertex3d)); delete pV3d; pV3d = NULL; return 0; } 输出:
我的理解是:
class Point有一个4bytes的vptr,该vptr中含有virtual destructor的地址;
class Point3d有一个4bytes的float member,由于virtual base class没有member,所以就没有一个vtable来存放virtual base class的offset,而Point3d本身也没有virtual function;
class Vertex同上;
class Vertex3d包含了一个Point3d和一个Vertex;

以上测试编译环境为GCC。在VC上测试,第一个例子的输出结果相同,第二个例子输出结果为:
4
8
8
12
说明即使virtual base class没有member,但是derived class还是用了一个vtable来存放virtua base class的offset。

你可能感兴趣的:(读书笔记 《深度探索c++对象模型》 (4))