一直想写一篇有关c 内存布局的文章,结合编译器vs2010来探讨c 对象模型在内存中的表现形式,主要是自己看《深度探索c 对象模型》太枯燥了,而且这本书也是较早出的,讨论的编译器都差不多过时了,所以才有这个想法,希望看官勿喷。废话少说,let's go...
没有虚函数的单继承
下面,我们假设有如下所示的单继承关系:
请注意,在这个继承关系中,父类,子类,都有自己的成员变量。而子类仅仅覆盖了父类的f()函数。
源代码如下:
class base { public: void f(){ cout<<"base::f()"<<endl; } void g(){ cout<<"base::g()"<<endl; } void h(){ cout<<"base::h()"<<endl; } private: char chbase; int ibase; }; class derive : public base { public: void f() { cout<<"derive::f()"<<endl; } void g1(){ cout<<"derive::g()"<<endl; } void h1(){ cout<<"derive::h()"<<endl; } private: char chderive; int iderive; };
这是没有虚函数的单继承,子类derive的f()函数会覆盖父类base的f()函数,当使用父类指针指向子类对象,并调用f()函数时,实际调用的是父类的f()函数。因为f()函数不是虚拟函数,编译器在编译的时候就确定声明什么类型的指针就调用什么类型的函数。很简单,这里就不举测试程序了。我们查看vc编译时,输出的信息可以看到父类和子类在内存中的布局如下:(注:这是vs2010输出的信息,下文输出这些类型信息也是)
1>class base size(8): 1> --- 1> 0 | chbase 1> |(size=3) 1> 4 | ibase 1> --- 1>class derive size(16): 1> --- 1> | --- (base class base) 1> 0 | | chbase 1> | | (size=3) 1> 4 | | ibase 1> | --- 1> 8 | chderive 1> | (size=3) 1>12 | iderive
可以看到:
1、成员变量根据其继承和声明顺序依次放在后面
2、成员函数定义在类体之外
3、调整字节对齐使bus的“运输量”达到最高效率
没有虚函数的多重继承
下面,我们假设有如下所示的多重继承关系:
在这个继承关系中,父类,子类,都有自己的成员变量。而了类分别覆盖了父类base1的一个函数f1()和父类base2的一个函数f2(),并且子类也新增一个函数h()。
源代码如下:
class base1 { public: base1(int ibase1) : ibase1(ibase1){} void f1(){ cout<<"base1::f1()"<<endl; } void g1(){ cout<<"base1::g1()"<<endl; } void h1(){ cout<<"base1::h1()"<<endl; } private: int ibase1; }; class base2 { public: base2(int ibase2) : ibase2(ibase2){} void f2(){ cout<<"base2::f2()"<<endl; } void g2(){ cout<<"base2::g2()"<<endl; } void h2(){ cout<<"base2::h2()"<<endl; } private: int ibase2; }; class derive : public base1, public base2 { public: derive(int ibase1, int ibase2, int iderive) : base1(ibase1), base2(ibase2), iderive(iderive) {} void f1(){ cout<<"derive::f1()"<<endl; } void g2(){ cout<<"derive::g2()"<<endl; } void h() { cout<<"derive::h()"<<endl; } private: int iderive; };
我们通过下面的程序来查看子类实例的内存布局:下面程序中,我使用了一个指针变量p指向子类的地址。
derive d(10, 20, 100); int *p = (int *)&d; cout<<endl; cout << " base1::" << endl; cout << " ibase1 = "<<*p <<endl;; cout << " base2::" << endl; cout << " ibase2 = "<<*p <<endl; cout << " derive::" << endl; cout << " iderive = "<<*p<运行结果如下:
父类和子类的内存布局在vc中的体现如下:
1>class base1 size(4): 1> --- 1> 0 | ibase1 1> --- 1>class base2 size(4): 1> --- 1> 0 | ibase2 1> --- 1>class derive size(12): 1> --- 1> | --- (base class base1) 1> 0 | | ibase1 1> | --- 1> | --- (base class base2) 1> 4 | | ibase2 1> | --- 1> 8 | iderive可以看到:
1、在没有虚函数的情况下,多重继承与单继承没有多大区别, 父类和父类的成员变量根据其继承和声明顺序依次存放后面,子类的成员变量放在最末尾。
有虚函数的单继承
下面,我们假设有如下所示的单继承关系:
在这个继承关系中,父类,子类,都有自己的成员变量。父类有3个虚函数,分别是f(),g(),h()。子类重写了父类的f()和g()函数,子类同时新增了虚函数h1()。
源代码如下:
class base { public: base(int ibase) : ibase(ibase){} virtual void f(){ cout<<"base::f()"<<endl; } virtual void g(){ cout<<"base::g()"<<endl; } virtual void h(){ cout<<"base::h()"<<endl; } private: int ibase; }; class derive : public base { public: derive(int ibase, int iderive) : base(ibase), iderive(iderive) {} virtual void f() { cout<<"derive::f()"<<endl; } virtual void g() { cout<<"derive::g()"<<endl; } virtual void h1(){ cout<<"derive::h1()"<<endl; } private: int iderive; };我们通过下面这个小程序来查看子类实例的内存布局:下面程序中,我使用了一个指向指针的指针变量pvtable指向子类的地址,后面的测试程序也这样来进行测试。
typedef void (*func)(); derive d(10, 100); int **pvtable = (int **)&d; func pfun = null; cout << " [0] base::vftable" << endl; pfun = (func)pvtable[0][0]; cout << " [0] "; pfun(); pfun = (func)pvtable[0][1]; cout << " [1] "; pfun(); pfun = (func)pvtable[0][2]; cout << " [2] "; pfun(); pfun = (func)pvtable[0][3]; cout << " [3] "; pfun(); pfun = (func)pvtable[0][4]; cout << " [4] "<endl; cout << " [1] base::ibase = "<<(int)pvtable[1]<<endl; cout << " [2] derive::iderive = "<<(int)pvtable[2]< 运行界面如下:
父类和子类在内存中的布局如下:
1>class base size(8): 1> --- 1> 0 | {vfptr} 1> 4 | ibase 1> --- 1>base::$vftable@: 1> | &base_meta 1> | 0 1> 0 | &base::f 1> 1 | &base::g 1> 2 | &base::h 1>class derive size(12): 1> --- 1> | --- (base class base) 1> 0 | | {vfptr} 1> 4 | | ibase 1> | --- 1> 8 | iderive 1> --- 1>derive::$vftable@: 1> | &derive_meta 1> | 0 1> 0 | &derive::f 1> 1 | &derive::g 1> 2 | &base::h 1> 3 | &derive::h1可以看出:
1、虚函数表在类的最开始位置
2、子类覆盖父类的虚函数在虚函数表得到体现
3、子类新增的虚函数添加到虚函数表的末尾
有虚函数的多继承
下面,我们假设有如下所示的多重继承关系:
在这个继承关系中,父类,子类,都有自己的成员变量。而子类多重继承父类base1和base2以及base3,都分别重写他们的一个虚函数,并且新增了两个虚函数derive_f1()和derive_g1()。
源代码:
class base1 { public: base1(int ibase1) : ibase1(ibase1){} virtual void f1(){ cout<<"base1::f1()"<<endl; } virtual void g1(){ cout<<"base1::g1()"<<endl; } virtual void h1(){ cout<<"base1::h1()"<<endl; } private: int ibase1; }; class base2 { public: base2(int ibase2) : ibase2(ibase2){} virtual void f2(){ cout<<"base2::f2()"<<endl; } virtual void g2(){ cout<<"base2::g2()"<<endl; } virtual void h2(){ cout<<"base2::h2()"<<endl; } private: int ibase2; }; class base3 { public: base3(int ibase3) : ibase3(ibase3){} virtual void f3(){ cout<<"base3::f3()"<<endl; } virtual void g3(){ cout<<"base3::g3()"<<endl; } virtual void h3(){ cout<<"base3::h3()"<<endl; } private: int ibase3; }; class derive : public base1, public base2, public base3 { public: derive(int ibase1, int ibase2, int ibase3, int iderive) : base1(ibase1), base2(ibase2), base3(ibase3), iderive(iderive) {} virtual void f1() { cout<<"derive::f()"<<endl; } virtual void g2() { cout<<"derive::g()"<<endl; } virtual void h3() { cout<<"derive::h1()"<<endl; } virtual void derive_f1(){ cout<<"derive::derive_f1()"<<endl; } virtual void derive_g1(){ cout<<"derive::derive_g1()"<<endl; } private: int iderive; };我们用下面代码来测试子类derive的布局情况:
typedef void (*func)(); derive d(10, 20 ,30, 100); int **pvtable = (int **)&d; func pfun = null; cout << " [0] base1::vftable" << endl; pfun = (func)pvtable[0][0]; cout << " [0] "; pfun(); pfun = (func)pvtable[0][1]; cout << " [1] "; pfun(); pfun = (func)pvtable[0][2]; cout << " [2] "; pfun(); pfun = (func)pvtable[0][3]; cout << " [3] "; pfun(); pfun = (func)pvtable[0][4]; cout << " [4] "; pfun(); pfun = (func)pvtable[0][5]; cout << " [5] "<endl; cout << " [1] base1::ibase1 = " <<(int)pvtable[1]<<endl; int pos = sizeof(base1)/4; cout << " ["< "] base2::vftable" << endl; pfun = (func)pvtable[pos][0]; cout << " [0] "; pfun(); pfun = (func)pvtable[pos][1]; cout << " [1] "; pfun(); pfun = (func)pvtable[pos][2]; cout << " [2] "; pfun(); pfun = (func)pvtable[0][3]; cout << " [3] "< endl; cout << " ["< 1<<"] base2::ibase2 = " <<(int)pvtable[pos 1]<<endl; pos = sizeof(base2)/4; cout << " ["< "] base3::vftable" << endl; pfun = (func)pvtable[pos][0]; cout << " [0] "; pfun(); pfun = (func)pvtable[pos][1]; cout << " [1] "; pfun(); pfun = (func)pvtable[pos][2]; cout << " [2] "; pfun(); pfun = (func)pvtable[0][3]; cout << " [3] "< endl; pos ; cout << " ["< "] base3::ibase3 = " <<(int)pvtable[pos]<<endl; pos ; cout << " ["< "] derive::iderive = " <<(int)pvtable[pos]< 运行界面如下:
vc输出窗口输出的子类内存的布局信息如下:
1>class derive size(28): 1> --- 1> | --- (base class base1) 1> 0 | | {vfptr} 1> 4 | | ibase1 1> | --- 1> | --- (base class base2) 1> 8 | | {vfptr} 1>12 | | ibase2 1> | --- 1> | --- (base class base3) 1>16 | | {vfptr} 1>20 | | ibase3 1> | --- 1>24 | iderive 1> --- 1>derive::$vftable@base1@: 1> | &derive_meta 1> | 0 1> 0 | &derive::f1 1> 1 | &base1::g1 1> 2 | &base1::h1 1> 3 | &derive::derive_f1 1> 4 | &derive::derive_g1 1>derive::$vftable@base2@: 1> | -8 1> 0 | &base2::f2 1> 1 | &derive::g2 1> 2 | &base2::h2 1>derive::$vftable@base3@: 1> | -16 1> 0 | &base3::f3 1> 1 | &base3::g3 1> 2 | &derive::h3可以看出:
1、在子类中的每一个父类都有一个虚函数表
2、子类新增的虚函数,按继承顺序放在第一个父类的虚函数表中
3、子类覆盖父类的虚函数在每一个父类的虚函数表得到体现
有虚函数的重复多继承
下面,我们假设有如下所示的多重继承关系:
孩子类grandderive多重继承于derive1和derive2。子类derive1和derive2继承同一个父类base。
源代码如下:
class base { public: base(int ibase) : ibase(ibase){} virtual void f(){ cout<<"base::f()"<<endl; } virtual void g(){ cout<<"base::g()"<<endl; } virtual void h(){ cout<<"base::h()"<<endl; } private: int ibase; }; class derive1 : public base { public: derive1(int ibase, int iderive1) : base(ibase), iderive1(iderive1) {} virtual void f() { cout<<"derive1::f()"<<endl; } virtual void g() { cout<<"derive1::g()"<<endl; } virtual void h1(){ cout<<"derive1::h1()"<<endl; } virtual void derive1_f1(){ cout<<"derive1::derive1_f1()"<<endl; } virtual void derive1_g1(){ cout<<"derive1::derive1_g1()"<<endl; } private: int iderive1; }; class derive2 : public base { public: derive2(int ibase, int iderive2) : base(ibase), iderive2(iderive2) {} virtual void f() { cout<<"derive2::f()"<<endl; } virtual void g() { cout<<"derive2::g()"<<endl; } virtual void h2(){ cout<<"derive2::h2()"<<endl; } virtual void derive2_f2(){ cout<<"derive2::derive2_f2()"<<endl; } virtual void derive2_g2(){ cout<<"derive2::derive2_g2()"<<endl; } private: int iderive2; }; class grandderive : public derive1, public derive2 { public: grandderive(int ibase, int iderive1, int iderive2, int igrandderive) : derive1(ibase, iderive1), derive2(ibase, iderive2), igrandderive(igrandderive) {} virtual void f() { cout<<"grandderive::f()"<<endl; } virtual void g() { cout<<"grandderive::g()"<<endl; } virtual void h() { cout<<"grandderive::h()"<<endl; } virtual void derive1_f1(){ cout<<"grandderive::derive1_f1()"<<endl; } virtual void derive2_g2(){ cout<<"grandderive::derive2_g2()"<<endl; } virtual void grandderive_f(){ cout<<"grandderive::grandderive_f()"<<endl; } virtual void grandderive_g(){ cout<<"grandderive::grandderive_g()"<<endl; } private: int igrandderive; };下面代码来测试孩子类grandderive的布局情况:
typedef void (*func)(); grandderive gd(10, 100 ,200, 1000); int **pvtable = (int **)&gd; func pfun = null; cout << " [0] derive1::vftable" << endl; pfun = (func)pvtable[0][0]; cout << " [0] "; pfun(); pfun = (func)pvtable[0][1]; cout << " [1] "; pfun(); pfun = (func)pvtable[0][2]; cout << " [2] "; pfun(); pfun = (func)pvtable[0][3]; cout << " [3] "; pfun(); pfun = (func)pvtable[0][4]; cout << " [4] "; pfun(); pfun = (func)pvtable[0][5]; cout << " [5] "; pfun(); pfun = (func)pvtable[0][6]; cout << " [6] "; pfun(); pfun = (func)pvtable[0][7]; cout << " [7] "; pfun(); pfun = (func)pvtable[0][8]; cout << " [8] "<endl; cout << " [1] base::ibase = " <<(int)pvtable[1]<<endl; cout << " [2] derive1::iderive1 = " <<(int)pvtable[2]<<endl; int pos = sizeof(derive1)/4; cout << " ["< "] derive2::vftable" << endl; pfun = (func)pvtable[pos][0]; cout << " [0] "; pfun(); pfun = (func)pvtable[pos][1]; cout << " [1] "; pfun(); pfun = (func)pvtable[pos][2]; cout << " [2] "; pfun(); pfun = (func)pvtable[pos][3]; cout << " [3] "; pfun(); pfun = (func)pvtable[pos][4]; cout << " [4] "; pfun(); pfun = (func)pvtable[pos][5]; cout << " [5] "; pfun(); pfun = (func)pvtable[pos][6]; cout << " [6] "< endl; pos ; cout << " ["< "] base::ibase = " <<(int)pvtable[pos]<<endl; pos ; cout << " ["< "] derive2::iderive2 = " <<(int)pvtable[pos]<<endl; pos ; cout << " ["< "] grandderive::igrandderive = " <<(int)pvtable[pos]< 运行界面如下:
孩子类的内存布局如下:
1>class grandderive size(28): 1> --- 1> | --- (base class derive1) 1> | | --- (base class base) 1> 0 | | | {vfptr} 1> 4 | | | ibase 1> | | --- 1> 8 | | iderive1 1> | --- 1> | --- (base class derive2) 1> | | --- (base class base) 1>12 | | | {vfptr} 1>16 | | | ibase 1> | | --- 1>20 | | iderive2 1> | --- 1>24 | igrandderive 1> --- 1>grandderive::$vftable@derive1@: 1> | &grandderive_meta 1> | 0 1> 0 | &grandderive::f 1> 1 | &grandderive::g 1> 2 | &grandderive::h 1> 3 | &derive1::h1 1> 4 | &grandderive::derive1_f1 1> 5 | &derive1::derive1_g1 1> 6 | &grandderive::grandderive_f 1> 7 | &grandderive::grandderive_g 1>grandderive::$vftable@derive2@: 1> | -12 1> 0 | &thunk: this-=12; goto grandderive::f 1> 1 | &thunk: this-=12; goto grandderive::g 1> 2 | &thunk: this-=12; goto grandderive::h 1> 3 | &derive2::h2 1> 4 | &derive2::derive2_f2 1> 5 | &grandderive::derive2_g2可以看出:
1、孩子类存在父类的两份拷贝。
2、孩子类的第二个父类的虚函数表,有些虚函数条目直接跳转到第一个父类的虚函数表的同名函数的,这只是vc的实现方式而已,其他编译器可能不是这样。
有虚函数的虚拟继承
下面,我们假设有如下所示的多重继承关系:
这个类图与"有虚函数的重复多继承"一致,主要区别在于derive1和derive2虚拟继承于base。
源代码如下:
class base { public: base(int ibase) : ibase(ibase){} virtual void f(){ cout<<"base::f()"<<endl; } virtual void g(){ cout<<"base::g()"<<endl; } virtual void h(){ cout<<"base::h()"<<endl; } private: int ibase; }; class derive1 : virtual public base { public: derive1(int ibase, int iderive1) : base(ibase), iderive1(iderive1) {} virtual void f() { cout<<"derive1::f()"<<endl; } virtual void g() { cout<<"derive1::g()"<<endl; } virtual void h1(){ cout<<"derive1::h1()"<<endl; } virtual void derive1_f1(){ cout<<"derive1::derive1_f1()"<<endl; } virtual void derive1_g1(){ cout<<"derive1::derive1_g1()"<<endl; } private: int iderive1; }; class derive2 :virtual public base { public: derive2(int ibase, int iderive2) : base(ibase), iderive2(iderive2) {} virtual void f() { cout<<"derive2::f()"<<endl; } virtual void g() { cout<<"derive2::g()"<<endl; } virtual void h2(){ cout<<"derive2::h2()"<<endl; } virtual void derive2_f2(){ cout<<"derive2::derive2_f2()"<<endl; } virtual void derive2_g2(){ cout<<"derive2::derive2_g2()"<<endl; } private: int iderive2; }; class grandderive : public derive1, public derive2 { public: grandderive(int ibase, int iderive1, int iderive2, int igrandderive) : base(ibase), derive1(ibase, iderive1), derive2(ibase, iderive2) , igrandderive(igrandderive) {} virtual void f() { cout<<"grandderive::f()"<<endl; } virtual void g() { cout<<"grandderive::g()"<<endl; } virtual void h() { cout<<"grandderive::h()"<<endl; } virtual void derive1_f1(){ cout<<"grandderive::derive1_f1()"<<endl; } virtual void derive2_g2(){ cout<<"grandderive::derive2_g2()"<<endl; } virtual void grandderive_f(){ cout<<"grandderive::grandderive_f()"<<endl; } virtual void grandderive_g(){ cout<<"grandderive::grandderive_g()"<<endl; } private: int igrandderive; };孩子类grandderive直接调用虚基类base的构造函数,子类derive1和derive2在初始化自身的时候不再调用基类base的构造函数。
我们使用下面程序代码来测试孩子类的布局情况:
typedef void (*func)(); grandderive gd(10, 100 ,200, 1000); int **pvtable = (int **)&gd; func pfun = null; cout << " [0] derive1::vftable" << endl; pfun = (func)pvtable[0][0]; cout << " [0] "; pfun(); pfun = (func)pvtable[0][1]; cout << " [1] "; pfun(); pfun = (func)pvtable[0][2]; cout << " [2] "; pfun(); pfun = (func)pvtable[0][3]; cout << " [3] "; pfun(); pfun = (func)pvtable[0][4]; cout << " [4] "; pfun(); pfun = (func)pvtable[0][5]; cout << " [5] "<endl; cout << " [1] derive1::vbase" <<*(pvtable[1])<<endl; cout << " [2] derive1::iderive1 = " <<(int)pvtable[2]<<endl; cout << " [3] derive2::vftable" << endl; pfun = (func)pvtable[3][0]; cout << " [0] "; pfun(); pfun = (func)pvtable[3][1]; cout << " [1] "; pfun(); pfun = (func)pvtable[3][2]; cout << " [2] "; pfun(); pfun = (func)pvtable[3][3]; cout << " [3] "< endl; cout << " [4] derive2::vbase" <<*(pvtable[4])<<endl; cout << " [5] derive2::iderive1 = " <<(int)pvtable[5]<<endl; cout << " [6] grandderive::igrandderive = " <<(int)pvtable[6]<<endl; cout << " [7] " <<(int)pvtable[7]; //这个不懂是什么 cout << " [8] base::vftable" << endl; pfun = (func)pvtable[8][0]; cout << " [0] "< //这里必须加上换行符,不然运行崩溃,不懂原因 pfun(); pfun = (func)pvtable[8][1]; cout << " [1] "< //这里必须加上换行符,不然运行崩溃,不懂原因 pfun(); pfun = (func)pvtable[8][2]; cout << " [2] "< //这里必须加上换行符,不然运行崩溃,不懂原因 pfun(); pfun = (func)pvtable[8][3]; cout << " [3] "<
cout << " [9] base::ibase = " <<(int)pvtable[9]<运行界面如下:
在上面的运行界面,我有两点疑问,已经用黄色标记,同时在上面的测试代码中我也用注释标明。如谁知道原因,麻烦告知,非常感谢。
下面是vc输出的grandderive的布局信息。
1> class grandderive size(40): 1> --- 1> | --- (base class derive1) 1> 0 | | {vfptr} 1> 4 | | {vbptr} 1> 8 | | iderive1 1> | --- 1> | --- (base class derive2) 1> 12 | | {vfptr} 1> 16 | | {vbptr} 1> 20 | | iderive2 1> | --- 1> 24 | igrandderive 1> --- 1> 28 | (vtordisp for vbase base) //上面就是这输出0, 1> --- (virtual base base) 1> 32 | {vfptr} 1> 36 | ibase 1> --- 1> 1> grandderive::$vftable@derive1@: 1> | &grandderive_meta 1> | 0 1> 0 | &derive1::h1 1> 1 | &grandderive::derive1_f1 1> 2 | &derive1::derive1_g1 1> 3 | &grandderive::grandderive_f 1> 4 | &grandderive::grandderive_g 1> 1> grandderive::$vftable@derive2@: 1> | -12 1> 0 | &derive2::h2 1> 1 | &derive2::derive2_f2 1> 2 | &grandderive::derive2_g2 1> 1> grandderive::$vbtable@derive1@: 1> 0 | -4 1> 1 | 28 (grandderived(derive1 4)base) 1> 1> grandderive::$vbtable@derive2@: 1> 0 | -4 1> 1 | 16 (grandderived(derive2 4)base) 1> 1> grandderive::$vftable@base@: 1> | -32 1> 0 | &(vtordisp) grandderive::f 1> 1 | &(vtordisp) grandderive::g 1> 2 | &(vtordisp) grandderive::h可以看出:
1、虚基类放在孩子类的末尾
2、vc实现虚基类的方式是每一个子类用一个指向虚基类表的指针vbptr,同时,子类的虚函数表不在包含父类的虚函数地址。
各个编译器实现的内存布局方式可能各不相同,在这里特指vs2010,如果在其他编译器运行上述代码,可能结果会不一样。
完!