【极客班】《c++面向对象高级编程下第二周》学习笔记

vtbl和vptr

下面简单图示说明使用gcc 4.6在x86 ubuntu上vptr和vtbl(以及VTT)布局。
关于VTT的参考链接:
http://stackoverflow.com/questions/6258559/what-is-the-vtt-for-a-class

根据我自己的验证,得到下面的结论:

  1. 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继承的差别):

#include 

class NoVirtualFunc{
 int a;
 double b;
 char c;
};

class VirtualFunc{
 int x;
 double y;
 char z;
public:
 virtual void vfunc1(){}
};

class PublicDerivedFromNoVirtualFunc: public NoVirtualFunc{
 int pdfnvf;
};

class PublicDerivedFromVirtualFunc: public VirtualFunc{
 int pdfvf;
};

class VirtualDerivedFromNoVirtualFunc: virtual NoVirtualFunc{
 int vdfnvf;
};

class VirtualDerivedFromVirtualFunc: virtual VirtualFunc{
 int vdfvf;
};

class PublicMultiple: public NoVirtualFunc, public VirtualFunc{
 int pm;
};

class VirtualMultiple: virtual  NoVirtualFunc, virtual VirtualFunc{
 int vm;
};

class AnotherVirtualFunc{
 char c;
public:
 virtual void g(){} 
};

class PublicMultipleTwoVirtual:public VirtualFunc, public AnotherVirtualFunc{
 char pmtv;
};

class VirutalMultipleTwoVirtual:virtual VirtualFunc, virtual AnotherVirtualFunc{
 char vmtv;
};

int main(void)
{
 NoVirtualFunc o1;
 VirtualFunc o2;
 
 PublicDerivedFromNoVirtualFunc o3;
 PublicDerivedFromVirtualFunc o4;
 
 VirtualDerivedFromNoVirtualFunc o5;
 VirtualDerivedFromVirtualFunc o6;
 
 PublicMultiple o7;
 VirtualMultiple o8;

 AnotherVirtualFunc o9;
 PublicMultipleTwoVirtual o10;
 VirutalMultipleTwoVirtual o11;
 
 return 0;
}

x86 ubuntu下, char、int、double分别占用1、4、8个字节,并且默认4字节对齐。可以使用下面命令打印出vtbl和vtt信息:

g++ -fdump-class-hierarchy base_derived.cpp

然后结合gdb打印各个字段的地址以及相关的指针地址,所画内存布局如下:

【极客班】《c++面向对象高级编程下第二周》学习笔记_第1张图片
pic1.png
【极客班】《c++面向对象高级编程下第二周》学习笔记_第2张图片
pic2.png
【极客班】《c++面向对象高级编程下第二周》学习笔记_第3张图片
pic3.png

在多重继承的情况下,如果多重继承的父类是同一个类,并且不是使用virtual继承,那么最终的子类中会存在顶端类内容的多份拷贝。
看下面代码:

class A {
  int x;
public:
  virtual void f(){}
};

class B: public A
{
  int y;
};

class C: public A
{
  int z;
};
class D: public B, public C
{
  int r;
};
int main(void)
{
  A a;
  B b;
  C c;
  D d;

  return 0;
}

其内存布局如下:

【极客班】《c++面向对象高级编程下第二周》学习笔记_第4张图片
pic4.PNG

但是如果采用virtual继承,那么祖先类数据在孙子类中只有一个一份。
代码如下:

class A {
  int x;
public:
  virtual void f(){}
};

class B: public virtual A
{
  int y;
};

class C: public virtual A
{
  int z;
};
class D: public B, public C
{
  int r;
};
int main(void)
{
  A a;
  B b;
  C c;
  D d;

  return 0;
}

内存布局如下:

this指针

c++对象都有一个this指针指向当前对象,讲义中侯捷老师讲解了MFC中的代码例子,如下图所示:


【极客班】《c++面向对象高级编程下第二周》学习笔记_第5张图片
np1.png

你可能感兴趣的:(【极客班】《c++面向对象高级编程下第二周》学习笔记)