面向对象高级编程(下)-- 第二周学习笔记(Boolan)
下面简单图示说明使用gcc 4.6在x86 ubuntu上vptr和vtbl(以及VTT)布局。
关于VTT的参考链接:
http://stackoverflow.com/questions/6258559/what-is-the-vtt-for-a-class
根据我自己的验证,得到下面的结论:
- x86 ubuntu上char类型为1字节,int为4个字节,double为8个字节,地址对齐方式是4字节对齐
2)如果一个类没有父类,并且没有虚函数,那么其对象的内存布局就是各个数据成员按定义顺序排列,并对其大小进行4字节对齐后得到的结果。没有vtbl,因而也没有vptr.
3)如果一个类没有父类,并且有虚函数,其对象开始地址会包含一个指向本类型vtbl中第一个虚函数的指针(vptr),接下来是其各个数据成员。vtbl中前面两项是0以及一个指向其typeinfo的指针,后面是虚函数的地址,而vptr指向自己vtbl中的第一个虚函数地址。
4)如果一个类是从一个父类public继承,并且父类中没有虚函数,其对象内存布局是其父类的数据成员,后面跟随的是本类的数据成员。因为父类和子类都没有虚函数,所以没有vtbl,因而没有vptr。
5)如果一个类是从一个父类public继承,并且父类中有虚函数,但是自己没有虚函数。那么其对象内存布局第一个位置是指向本类型vtbl中第一个虚函数的偏移地址的指针。其vtbl中前面两项分别是0和指向自己typeinfo的指针,第三项开始是其虚函数的偏移地址。
6)如果一个类从父类virtual继承,那么还会生成VTT表(实际上其中包含指向其vtbl偏移地址的指针)。
7)多重继承的情况下,其内存布局是从基类内存布局依次排列,然后后面跟随自己的虚函数指针(假设有虚函数)和其数据成员。
测试代码如下(主要考察是否有虚函数以及public和virtual继承的差别):
1 #include <iostream> 2 3 class NoVirtualFunc{ 4 int a; 5 double b; 6 char c; 7 }; 8 9 class VirtualFunc{ 10 int x; 11 double y; 12 char z; 13 public: 14 virtual void vfunc1(){} 15 }; 16 17 class PublicDerivedFromNoVirtualFunc: public NoVirtualFunc{ 18 int pdfnvf; 19 }; 20 21 class PublicDerivedFromVirtualFunc: public VirtualFunc{ 22 int pdfvf; 23 }; 24 25 class VirtualDerivedFromNoVirtualFunc: virtual NoVirtualFunc{ 26 int vdfnvf; 27 }; 28 29 class VirtualDerivedFromVirtualFunc: virtual VirtualFunc{ 30 int vdfvf; 31 }; 32 33 class PublicMultiple: public NoVirtualFunc, public VirtualFunc{ 34 int pm; 35 }; 36 37 class VirtualMultiple: virtual NoVirtualFunc, virtual VirtualFunc{ 38 int vm; 39 }; 40 41 class AnotherVirtualFunc{ 42 char c; 43 public: 44 virtual void g(){} 45 }; 46 47 class PublicMultipleTwoVirtual:public VirtualFunc, public AnotherVirtualFunc{ 48 char pmtv; 49 }; 50 51 class VirutalMultipleTwoVirtual:virtual VirtualFunc, virtual AnotherVirtualFunc{ 52 char vmtv; 53 }; 54 55 int main(void) 56 { 57 NoVirtualFunc o1; 58 VirtualFunc o2; 59 60 PublicDerivedFromNoVirtualFunc o3; 61 PublicDerivedFromVirtualFunc o4; 62 63 VirtualDerivedFromNoVirtualFunc o5; 64 VirtualDerivedFromVirtualFunc o6; 65 66 PublicMultiple o7; 67 VirtualMultiple o8; 68 69 AnotherVirtualFunc o9; 70 PublicMultipleTwoVirtual o10; 71 VirutalMultipleTwoVirtual o11; 72 73 return 0; 74 }
const
const成员函数
const可以修饰成员函数来避免成员函数对this指针的修改。
当成员函数的const和non-const版本都存在时,const成员只能调用const版本,
non-const成员只能调用non-const版本。
new&delete
我们可以对operator new和operator delete进行各种版本的重载,但是每个版本都需要有自己的独特参数序列
其中第一个参数必须是size_t.
对于operator delete的各种重载版本,它们虽然可以被重载,但是不会被delete调用,只有当new 调用的构造函数抛出异常时,它们才会被调用,用来清除申请失败对象的内存。
关于operator new和 operator delete的具体讨论见这次作业。operator new与 delete重载
当然我们也可以通过使用域作用符::来强制使用全局new和全局delete,调用方法是::new和::delete