C++讲解.doc

上传人:hw****26 文档编号:3539921 上传时间:2019-06-03 格式:DOC 页数:23 大小:264KB
下载 相关 举报
C++讲解.doc_第1页
第1页 / 共23页
C++讲解.doc_第2页
第2页 / 共23页
C++讲解.doc_第3页
第3页 / 共23页
C++讲解.doc_第4页
第4页 / 共23页
C++讲解.doc_第5页
第5页 / 共23页
点击查看更多>>
资源描述

1、 读书笔记 深度探索 c+对象模型 (1) 收藏第二章:构造函数语意学2.1 default constructor 的构建操作c+标准说:对于 class X,如果没有任何程序员声明的构造函数,那么编译器会 implicitly(暗中)合成出一个 trivial(无能的,没啥用的)构造函数。(备注:global objects 的内存在程序激活的时候会被清 0。local objects 位于程序的堆栈中,heap objects 位于自由空间中,都不一定会被清 0。它们的内容是内存上次被使用之后的遗迹。)但是,有四种情况,这个被 implicitly 合成出的函数,会是 nontriviv

2、al(有用的)。a)带有 default constructor 的 member class object例如:view plaincopy to clipboardprint?1. class A 2. public: 3. A(); 4. ; 5. 6. class B 7. public: 8. private: 9. A a; 10. int i; 11. ; 12. 13. B:B() 14. a.A:A(); 15. 被合成的 class B 的 default constructior 内含有必要的代码,这些代码调用了class A 的 default constructor。

3、但是,并不会产生初始化 i 的代码。所以说,被合成的 default constructor 只符合编译器的需要,不符合程序员的需要。如果 B 已经有程序员定义的构造函数,那么每个构造函数都会被编译器扩张,在其中安插一些代码,这些代码在 user code 执行之前,会调用必要的 default constructor。并且,如果 class B 中有多个 class member objects 需要 constructor 的初始化操作。那么 c+会按照声明的顺序来调用各个 constructor。例如:view plaincopy to clipboardprint?1. class A

4、 2. public: 3. A(); 4. ; 5. 6. class B 7. public: 8. B(); 9. B(int x):b(x); 10. private: 11. int b; 12. ; 13. 14. class C 15. public: 16. C(); 17. ; 18. 19. class D 20. public: 21. D():b(1024) 22. d = 2048; 23. 24. private: 25. A a; 26. B b; 27. C c; 28. int d; 29. ; 30. 31. class D 的构造函数会被扩展成:view

5、plaincopy to clipboardprint?1. D:D():b(1024) 2. a.A:A(); 3. b.B:B(1024); 4. c.C:C(); 5. 6. d = 2048; 7. b)带有 default constructor 的 base class类似,如果一个没有任何 constructor 的 class 派生自一个带有 default constructor 的基类,那么一个 nontrivial 的 constructor 会被合成出来。它将调用上层基类的 default constructor(按照声明的次序) 。如果程序员提供了多个 constru

6、ctor,但是没有 default constructor,那么每一个constructor 都会被扩张会将调用基类 default constructor 的代码加进去。如果同时存在带有 default constructor 的 member class object,那么这些 member 的 default constructor 也会被调用在所有 base class constructor 被调用之后。c)带有一个 virtual function 的 class我们知道,一个带有 virtual function 的 class 的每一个 object 中,一个 vptr 会被合

7、成出来,内含相关 class vtbl 的地址。为了让多态的机制发挥功效,编译器会为 vptr 设定初始值。对于每一个 constructor,它们会被安插一些代码来做这件事情。如果没有声明任何 constructor,那么合成的 default constructor 会初始化每一个 class object 的vptr。d)带有 virtual base class 的 classvirtual base class 的实现在不同的编译器中有很大的差异。但是它们的共同点是:必须使每一个 virtual base class 在其每一个 derived class object 中的位置,必

8、须在执行期准备妥当。view plaincopy to clipboardprint?1. class X public x; ; 2. class A : public virtual public : int a; ; 3. class B : public virtual public : int b; ; 4. class C : public A, public B public : int c; ; 5. void foo(const A* pa) 6. pa-i = 1024; 7. 8. int main() 9. foo(new A); 10. foo(new C); 11.

9、 return 0; 12. 编译器是无法决定 foo 中 i 的偏移位置的。因为 pa 的真正类型在执行期间可以改变。所以编译器必须改变一些代码使得 X:x 的位置可以在执行期才被决定。一种做法是:在derived class object 的每一个 virtual base class 中安插一个指针,表示在当前object 中,该 virtual base class 的位置。view plaincopy to clipboardprint?1. void foo(const A* pa) 2. pa-_vbcX-i = 1024; 3. _vbcX 是编译器产生的指针,指向 virtu

10、al base class X。这个指针是在编译时期构建的。对于 class 定义的每一个 constructor,编译器会安插那些 “允许每一个 virtual base class 的执行期存取操作”的代码。如果 class 没有声明任何 constructor,那么编译器会合成一个 default constructor 来做这些事情。这个 default constructor 就是nontrivial 的。c)和 d)其实是差不多的,分别是初始化 virtual function 机制和 virtual base class 机制。第三章:data 语意学3.4 “继承”与 Data

11、 Membera) 只要继承不要多态view plaincopy to clipboardprint?1. #include 2. #include 3. using namespace std; 4. 5. class Empty 6. public: 7. ; 8. 9. class Concrete1 10. public: 11. /. 12. private: 13. int val; 14. char bit1; 15. ; 16. 17. class Concrete2 : public Concrete1 18. public: 19. /. 20. private: 21.

12、char bit2; 22. ; 23. 24. class Concrete3 : public Concrete2 25. public: 26. /. 27. private: 28. char bit3; 29. ; 30. 31. int main () 32. printf(“%dn“, sizeof(Empty); 33. printf(“%dn“, sizeof(Concrete1); 34. printf(“%dn“, sizeof(Concrete2); 35. printf(“%dn“, sizeof(Concrete3); 36. return 0; 37. 我在 GC

13、C 上输出的结果是:1888在 VC 上输出的结果是:181216照书上的分析和我自己的分析应该是后者(Concrete2 的 bit2 被放在了 Concrete1 的补齐空间之后,再加上自己的补齐,所以占用了 12 个 byte,依此类推) ,不明白 GCC 是做了什么处理。b) 加上多态虚指针放在头部或者尾部。c) 多重继承“最左端”的基类和派生类的地址相同。d) 虚拟继承class 中如果含有虚基类,将被分割成两个部分:一个不变局部和一个共享局部。共享局部即是虚基类部分,其位置会因为每次派生的操作会有变化,所以它们只能被间接存取。各家编译器实现的差异就是间接存取的方法,下面介绍三种主要

14、的存取方法:1. 在每一个派生类对象中为每一个虚基类安插一些指针,每个指针指向一个虚基类。2. 每一个 class object 如果有一个或者多个虚基类,那么会有一个指针指向一个 virtual base class table,这个表格中存放的是真正的指向虚基类的指针。3. 在 virtual function table 中放置虚基类的 offset,而不是地址。3.4 指向 “Data Members”的指针view plaincopy to clipboardprint?1. #include 2. #include 3. using namespace std; 4. 5. cla

15、ss Point3d 6. public: 7. virtual Point3d() 8. 9. 10. public: 11. static Point3d origin; 12. float x, y, z; 13. ; 14. 15. int main () 16. printf(“ 17. printf(“ 18. printf(“ 19. 20. return 0; 21. 在 GCC 中输出的结果是:48C说明虚指针是放在开头的。这里注意的是 float Point3d:*p2 是一个指向对象成员的指针。所以 4. virtual Base1(); 5. virtual void

16、SpeakClearly(); 6. virtual Base1* clone() const; 7. protected: 8. float data_Base1; 9. ; 10. 11. class Base2 12. public: 13. Base2(); 14. virtual Base2(); 15. virtual void mumble(); 16. virtual Base2* clone() const; 17. protected: 18. float data_Base2; 19. ; 20. 21. class Derived : public Base1, pub

17、lic Base2 22. public: 23. Derived(); 24. virtual Derived(); 25. virtual Derived* clone() const; 26. protected: 27. float data_Derived; 28. ; 有如上派生关系,如果有:view plaincopy to clipboardprint?1. Base2 *pbase2 = new Derived; 2. delete pbase2; 那么第一行会将 pbase 指针调整以指向 Base2 subobject。否则的话,类似view plaincopy to c

18、lipboardprint?1. pbase2-data_Base2; 这种非多态应用都会失败。而在第三行的时候,由于要调用 Derived 的析构,所以 this指针又会被调整到 Derived 对象开头。其实现是运用了一种叫 Thunk 的技术,在代码里面插了一小段汇编代码,用来做两件事情:调整 this 指针的位置,跳转到 virtual function 的位置。于是,对于多重继承来说,有多少个 base class,就有多少个 vptr,分别指向多少张virtual table。每个 virtual table 的内容参见深度探索 c+对象模型P165 页的图。注意,由于 base_

19、1 和 derived class 共享一个 virtual table,所以该virtual table 内的内容最多。甚至包括了 Base_2:mumble。不过Base_2:mumble 的真正地址存放在 Base_2 对应的 virtual table 中。所以想要通过 derived class 的指针条用 mumble 方法,需要利用 thunk 调整 this 指针,这就是下面三种情况的第 2 种。 (注意,某 base class 的指针,new 出来的对象是derived 的,也不能调用其他 base class 的方法)指针的类型是什么,就使用那个基类相应的 virtua

20、table。派生类和第一个 base class 使用的是同一个 virtual table。有三种情况,第二或者后继的 base class 会影响到 virtual functions 的支持。这时候需要利用 thunk 调整 this 指针。view plaincopy to clipboardprint?1. / 情况 1,通过一个指向第二个 base class 的指针调用一个derived class virtual function 2. Base2 *pbase2 = new Derived; 3. / 调用Derived() , pbase2 必须向后调整 sizeof(Ba

21、se1)个字节 4. delete pbase2; 5. 6. / 情况 2,通过一个指向 Derived class 的指针调用一个继承自第二个 base class 的virtual function 7. Derived *pderived = new Derived; 8. / pderived 必须向前调整 sizeof(Base1)个字节 9. pderived-mumble(); 10. 11. / 情况 3,先向后调整 sizeof(Base1)个字节 12. Base2 *pbase21 = new Derived; 13. / 调用 clone 方法时,pbase21 调整

22、到 Derived 对象开头,于是 clone 的 Derived 版本会被调用,它会返回一个 Derived 的指针,然后再被向后调整到指向 Base2 SubObject 14. Base2 *pbase22 = pbase21-clone(); c) 虚拟继承下的 virtual functionsview plaincopy to clipboardprint?1. class Point2d 2. public: 3. Point2d(); 4. virtual Point2d(); 5. virtual void mumble(); 6. virtual float z(); 7.

23、 protected: 8. float _x, _y; 9. ; 10. 11. class Point3d : public virtual Point2d 12. public: 13. Point3d(); 14. Point3d(); 15. float z(); 16. protected: 17. float _z; 18. ; 由于 Point2d 和 Point3d 的起始位置不一样,所以两者之间的转换也需要做 this 指针调整。所以对于 Point3d object 来说(编译器是 VC),有两张 virtual table,一个是继承下来的,里面内容 z()和析构函数变

24、成了 derived class 的 z()和析构。mumble()应该保持不变。另一长 virtual table 里面多了一个 virtual base class object 的 offset,其余一样。 (见深度探索 c+对象模型 P169 页的图)不过有些编译器如 GCC 则是将这个 offset 加到第一张 table 中,于是 GCC 编译器编译的结果也只有一张 virtual table。读书笔记 深度探索 c+对象模型 (4) 收藏第五章:构造,解构,拷贝语意学5.2 继承体系下的对象构造a) 虚拟继承如同下面的继承情况:view plaincopy to clipboar

25、dprint?1. #include 2. #include 3. using namespace std; 4. class Point 5. public: 6. Point() 7. printf(“Point : constructorn“); 8. 9. virtual Point() 10. printf(“Point : destructorn“); 11. 12. protected: 13. float _x, _y; 14. ; 15. 16. class Point3d : public virtual Point 17. public: 18. Point3d() 19

26、. printf(“Point3d : constructorn“); 20. 21. Point3d() 22. printf(“Point3d : destructorn“); 23. 24. protected: 25. float _z; 26. ; 27. 28. class Vertex : public virtual Point 29. public: 30. Vertex() 31. printf(“Vertex : constructorn“); 32. 33. Vertex() 34. printf(“Vertex : destructorn“); 35. 36. pro

27、tected: 37. float _w; 38. ; 39. 40. class Vertex3d : public Point3d, public Vertex 41. public: 42. Vertex3d() 43. printf(“Vertex3d : constructorn“); 44. 45. Vertex3d() 46. printf(“Vertex3d : destructorn“); 47. 48. protected: 49. float _u; 50. ; 51. 52. int main () 53. 54. Vertex3d *pV3d = new Vertex3d; 55. delete pV3d; 56. pV3d = NULL; 57. return 0; 58. 在这种情况下,Vertex3d 压制了它的两个父类对 Point constructor 的调用,这个调用工作由 Vertext3d 本身来完成。所以输出为:

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 教育教学资料库 > 精品笔记

Copyright © 2018-2021 Wenke99.com All rights reserved

工信部备案号浙ICP备20026746号-2  

公安局备案号:浙公网安备33038302330469号

本站为C2C交文档易平台,即用户上传的文档直接卖给下载用户,本站只是网络服务中间平台,所有原创文档下载所得归上传人所有,若您发现上传作品侵犯了您的权利,请立刻联系网站客服并提供证据,平台将在3个工作日内予以改正。