深入理解C++对象模型-对象的内存布局,vptr,vtable

来源:互联网 发布:从底层看java中的接口 编辑:程序博客网 时间:2024/06/06 18:58

vtpr的位置:
为了支持多态,C++引入了vtpr和vtable这两个概念.对于每个有虚函数的类,C++都会为其生成一个vtable,并在类中添加一个隐含的数据成员vptr. 对于vptr在对象中的位置,跟类的数据成员的布局一样,C++标准里面并没有做出任何的规定.但是对于特定的编译器,我们还是可以通过研究C++对象的内存布局来确定vtpr到底是放在哪里.

下面我们通过分析C++对象的内存布局,来寻找vptr的位置.在开始讨论之前我们先提供一个模板函数void PrintLayout(T const & obj),该函数用于打印obj所在内存的内容,下面是该函数的实现:

[cpp] view plain copy
  1. //PrintLayout.hxx  
  2.   
  3. #pragma once  
  4. #include <iostream>  
  5. #include <iomanip>  
  6. #include <ReinterpretCast.hxx>  
  7.   
  8. template<typename T>  
  9. void PrintLayout(T const & obj)  
  10. {  
  11.     int * pObj = ReinterpretCast<int*>(&obj);  
  12.     for (int i =0; i<sizeof(obj)/sizeof(int);++i)  
  13.     {  
  14.         std::cout<<std::setw(10)<< pObj[i]<<std::endl;  
  15.     }  
  16. }  

 

接下来通过代码来分析一下在C++里,在没有继承,单继承,多继承以及虚继承等情况下对象的内存布局,下面是示例代码,为了减少代码量,我们将类的所有数据成员设为public的,在这里我们用struct来代替class:

[c-sharp] view plain copy
  1. //main.cpp  
  2.  
  3. #include <iostream>  
  4. #include <PrintLayout.hxx>  
  5. #include <typeinfo>  
  6. using namespace std;  
  7.   
  8. struct NoVirtualMemFunc  
  9. {  
  10.     int  Func1(int a,int b){  
  11.         cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;  
  12.         return 0;  
  13.     }  
  14.     int  Func2(int a,int b){  
  15.         cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;  
  16.         return 0;  
  17.     }  
  18.     int m_iData;  
  19. };  
  20. struct Base1  
  21. {  
  22.     virtual int  Base1Func1(int a,int b){  
  23.         cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;  
  24.         return 0;  
  25.     }  
  26.     virtual int  Base1Func2(int a,int b){  
  27.         cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;  
  28.         return 0;  
  29.     }  
  30.     int m_iData;  
  31. };  
  32. struct Base2  
  33. {  
  34.     virtual int  Base2Func1(int a,int b){  
  35.         cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;  
  36.         return 0;  
  37.     }  
  38.     virtual int  Base2Func2(int a,int b){  
  39.         cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;  
  40.         return 0;  
  41.     }  
  42.     int m_iData;  
  43. };  
  44.   
  45. struct D1:public Base1  
  46. {  
  47.     virtual int  D1Func(int a,int b){  
  48.         cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;  
  49.         return 0;  
  50.     }  
  51.     int m_iData;  
  52. };  
  53. struct D:public Base1,public Base2  
  54. {  
  55.     virtual int  DFunc(int a,int b){  
  56.         cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;  
  57.         return 0;  
  58.     }  
  59.     int m_iData;  
  60. };  
  61.   
  62. struct VD1:public virtual Base1  
  63. {  
  64.     virtual int  VD1Func(int a,int b){  
  65.         cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;  
  66.         return 0;  
  67.     }  
  68.     int m_iData;  
  69. };  
  70. struct VD2:public virtual Base1  
  71. {  
  72.     virtual int  D2Func(int a,int b){  
  73.         cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;  
  74.         return 0;  
  75.     }  
  76.     int m_iData;  
  77. };  
  78. struct VD:public VD1,public VD2  
  79. {  
  80.     int m_iData;  
  81. };  
  82.   
  83.   
  84.   
  85. template<typename T>  
  86. void PRINT_LAYOUT(T const & obj)  
  87. {  
  88.     cout<<"The layout of "<<typeid(obj).name()<<"----------------"<<endl;  
  89.     PrintLayout(obj);  
  90.     cout<<endl;  
  91. }  
  92. int main(int argc, char* argv[])  
  93. {  
  94.     //没有继承,没有虚函数的情况  
  95.     {  
  96.         NoVirtualMemFunc obj;  
  97.         obj.m_iData = 100;  
  98.         PRINT_LAYOUT(obj);  
  99.     }  
  100.       
  101.   
  102.     //没有继承,有虚函数的情况  
  103.     {  
  104.         Base1 obj;  
  105.         obj.m_iData = 100;  
  106.         PRINT_LAYOUT(obj);  
  107.     }  
  108.       
  109.   
  110.     //单继承  
  111.     {  
  112.         D1 obj;  
  113.         obj.Base1::m_iData = 100;  
  114.         obj.m_iData = 200;  
  115.         PRINT_LAYOUT(obj);  
  116.     }  
  117.       
  118.   
  119.     //多继承  
  120.     {  
  121.         D obj;  
  122.         obj.Base1::m_iData  = 100;  
  123.         obj.Base2::m_iData  = 200;  
  124.         obj.m_iData = 300;  
  125.         PRINT_LAYOUT(obj);  
  126.     }  
  127.       
  128.   
  129.     //虚拟继承  
  130.     {  
  131.         VD1 obj;  
  132.         obj.Base1::m_iData = 100;  
  133.         obj.m_iData = 200;  
  134.         PRINT_LAYOUT(obj);  
  135.     }  
  136.       
  137.   
  138.     //棱形继承  
  139.     {  
  140.         VD obj;  
  141.         obj.Base1::m_iData = 100;  
  142.         obj.VD1::m_iData   = 200;  
  143.         obj.VD2::m_iData   = 300;  
  144.         obj.m_iData       = 500;  
  145.         PRINT_LAYOUT(obj);  
  146.   
  147.     }  
  148.     return 0;  
  149. }  
  150.   
  151. //输出  
  152. /* 
  153. The layout of struct NoVirtualMemFunc---------------- 
  154.       100 
  155.  
  156. The layout of struct Base1---------------- 
  157.    4294656 
  158.        100 
  159.  
  160. The layout of struct D1---------------- 
  161.    4294740 
  162.        100 
  163.        200 
  164.  
  165. The layout of struct D---------------- 
  166.    4294800 
  167.        100 
  168.    4294776 
  169.        200 
  170.        300 
  171.  
  172. The layout of struct VD1---------------- 
  173.    4294876 
  174.    4294888 
  175.        200 
  176.    4294864 
  177.        100 
  178.  
  179. The layout of struct VD---------------- 
  180.    4294944 
  181.    4294968 
  182.        200 
  183.    4294932 
  184.    4294952 
  185.        300 
  186.        500 
  187.    4294920 
  188.        100 
  189.  
  190. 请按任意键继续. . . 
  191. */  

对于有虚表的函数,从上面的输出我们可以得到以下结论,

1.没有继承情况,vptr存放在对象的开始位置,以下是Base1的内存布局

vptr : 4294656

m_iData : 100

2.单继承的情况下,对象只有一个vptr,它存放在对象的开始位置,派生类子对象在父类子对象的最后面,以下是D1的内存布局

 

vptr : 4294740

B1:: m_iData : 100

B2:: m_iData :200

3.多继承情况下,对象会为每个有虚函数的父类子对象提供一个vptr,派生类子对象在所有父类子对象的最后面,所有父类子对象按照声明顺序排列,以下是D的内存布局

B1::vptr : 4294800

B1::m_iData : 100

B2::vptr : 4294776

B2::m_iData : 200

D::m_iData : 300

4. 虚拟继承情况下,虚父类子对象会放在派生类子对象之后,派生类子对象的第一个位置存放着一个vptr,虚拟子类子对象也会保存一个vptr,以下是VD1的内存布局 

VD1::vptr : 4294876

 Unknown : 4294888

VD1::m_iData : 200

B1::vptr : 4294864

B1::m_iData :100

5. 棱形继承的情况下,非虚基类子对象在派生类子对象前面,并按照声明顺序排列,虚基类子对象在派生类子对象后面

VD1::vptr :          4294944

VD1::Unknown : 4294968

VD1::m_iData :  200

VD2::vptr :     4   294932

VD2::Unknown : 4294952

VD2::m_iData :  300

VD::m_iData :  500

B1::vptr :         4294920

B1::m_iData :   100

接下来我们将通过代码来验证前面结论的准确性.下面的代码具有一定的局限性.在调试以下代码的时候,对虚拟继承遇到了以下几个让我迷惑的问题:

1.对于虚拟继承,函数指针的大小为12

2.用vtable里面的指针调用,this不能正确传进去

3.如果派生类的虚拟函数多于1个,则会crash

 

 

[cpp] view plain copy
  1. //main.cpp  
  2.   
  3. #include <iostream>  
  4. #include <GetVptr.hxx>  
  5. #include <typeinfo>  
  6. using namespace std;  
  7.   
  8. struct NoVirtualMemFunc  
  9. {  
  10.     int  Func1(int a,int b){  
  11.         cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;  
  12.         return 0;  
  13.     }  
  14.     int  Func2(int a,int b){  
  15.         cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;  
  16.         return 0;  
  17.     }  
  18.     int m_iData;  
  19. };  
  20. struct Base1  
  21. {  
  22.     virtual int  Base1Func1(int a,int b){  
  23.         cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;  
  24.         return 0;  
  25.     }  
  26.     virtual int  Base1Func2(int a,int b){  
  27.         cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;  
  28.         return 0;  
  29.     }  
  30.     int m_iData;  
  31. };  
  32. struct Base2  
  33. {  
  34.     virtual int  Base2Func1(int a,int b){  
  35.         cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;  
  36.         return 0;  
  37.     }  
  38.     virtual int  Base2Func2(int a,int b){  
  39.         cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;  
  40.         return 0;  
  41.     }  
  42.     int m_iData;  
  43. };  
  44.   
  45. struct D1:public Base1  
  46. {  
  47.     virtual int  D1Func(int a,int b){  
  48.         cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;  
  49.         return 0;  
  50.     }  
  51.     int m_iData;  
  52. };  
  53. struct D:public Base1,public Base2  
  54. {  
  55.     virtual int  DFunc(int a,int b){  
  56.         cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;  
  57.         return 0;  
  58.     }  
  59.     int m_iData;  
  60. };  
  61.   
  62. struct VD1:public virtual Base1  
  63. {  
  64.     virtual int  VD1Func(int a,int b){  
  65.         cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;  
  66.         return 0;  
  67.     }  
  68.     int m_iData;  
  69. };  
  70. struct VD2:public virtual Base1  
  71. {  
  72.     virtual int  D2Func(int a,int b){  
  73.         cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;  
  74.         return 0;  
  75.     }  
  76.     int m_iData;  
  77. };  
  78. struct VD:public VD1,public VD2  
  79. {  
  80.     int m_iData;  
  81. };  
  82.   
  83. template<class T>  
  84. struct MemFuncT  
  85. {  
  86.     typedef int (T::* T_MemFuncT)(int,int);  
  87.     typedef int (T::* T_MemDataT);  
  88. };  
  89. template<class C>  
  90. void CallMemFunc(int iFuncNum,int (C::**vptr)(int,int),C& obj,int a =500,int b =600)  
  91. {  
  92.     for (int i =0;i<iFuncNum;++i)  
  93.     {  
  94.         //cout<<ReinterpretCast<void*>(vptr[i])<<"   ";  
  95.         (obj.*vptr[i])(a,b);  
  96.     }  
  97.     cout<<endl;  
  98. }  
  99. int main(int argc, char* argv[])  
  100. {  
  101.   
  102.   
  103.     //没有继承,有虚函数的情况  
  104.     {  
  105.         cout<<"//没有继承,有虚函数的情况"<<endl;  
  106.         Base1 obj;  
  107.         obj.m_iData = 100;  
  108.         MemFuncT<Base1>::T_MemFuncT * vptr = ReinterpretCast<MemFuncT<Base1>::T_MemFuncT *>(GetVptr(obj));  
  109.         CallMemFunc(2,vptr,obj);  
  110.     }  
  111.       
  112.   
  113.     //单继承  
  114.     {  
  115.         cout<<"//单继承"<<endl;  
  116.         D1 obj;  
  117.         obj.Base1::m_iData = 100;  
  118.         obj.m_iData = 200;  
  119.         MemFuncT<D1>::T_MemFuncT * vptr = ReinterpretCast<MemFuncT<D1>::T_MemFuncT *>(GetVptr(obj));  
  120.         CallMemFunc(3,vptr,obj);  
  121.     }  
  122.       
  123.   
  124.     //多继承  
  125.     {  
  126.         cout<<"//多继承"<<endl;  
  127.         D obj;  
  128.         obj.Base1::m_iData  = 100;  
  129.         obj.Base2::m_iData  = 200;  
  130.         obj.m_iData = 300;  
  131.   
  132.         Base1 &objB1 = obj;  
  133.         MemFuncT<Base1>::T_MemFuncT * vptr = ReinterpretCast<MemFuncT<Base1>::T_MemFuncT *>(GetVptr(obj));  
  134.         CallMemFunc(3,vptr,objB1);  
  135.   
  136.         Base2 &objB2 = obj;  
  137.         MemFuncT<Base2>::T_MemFuncT * vptrB2 = ReinterpretCast<MemFuncT<Base2>::T_MemFuncT *>(GetVptr(objB2));  
  138.         CallMemFunc(2,vptrB2,objB2);  
  139.     }  
  140.       
  141. #if 1  
  142.     //虚拟继承  
  143.     {  
  144.         cout<<"//虚拟继承"<<endl;  
  145.         VD1 obj;  
  146.         obj.Base1::m_iData = 100;  
  147.         obj.m_iData = 200;  
  148.         MemFuncT<VD1>::T_MemFuncT * vptr = ReinterpretCast<MemFuncT<VD1>::T_MemFuncT *>(GetVptr(obj));  
  149.         CallMemFunc(1,vptr,obj);  
  150.   
  151.         Base1 & objB1 =obj ;  
  152.         MemFuncT<Base1>::T_MemFuncT * vptrB1 = ReinterpretCast<MemFuncT<Base1>::T_MemFuncT *>(GetVptr(objB1));  
  153.         CallMemFunc(2,vptrB1,objB1);  
  154.     }  
  155.       
  156.   
  157.     //棱形继承  
  158.     {  
  159.         cout<<"//棱形继承"<<endl;  
  160.         VD obj;  
  161.         obj.Base1::m_iData = 100;  
  162.         obj.VD1::m_iData   = 200;  
  163.         obj.VD2::m_iData   = 300;  
  164.         obj.m_iData       = 500;  
  165.   
  166.         Base1 & objB1 = obj;  
  167.         MemFuncT<Base1>::T_MemFuncT * vptrB1 = ReinterpretCast<MemFuncT<Base1>::T_MemFuncT *>(GetVptr(objB1));  
  168.         CallMemFunc(2,vptrB1,objB1);  
  169.   
  170.         VD1   & objVD1 =obj;  
  171.         MemFuncT<VD1>::T_MemFuncT * vptrVD1 = ReinterpretCast<MemFuncT<VD1>::T_MemFuncT *>(GetVptr(objVD1));  
  172.         CallMemFunc(1,vptrVD1,objVD1);  
  173.   
  174.         VD2   & objVD2 =obj;  
  175.         MemFuncT<VD2>::T_MemFuncT * vptrVD2 = ReinterpretCast<MemFuncT<VD2>::T_MemFuncT *>(GetVptr(objVD2));  
  176.         //CallMemFunc(1,vptrVD2,objVD2);  
  177.     }  
  178. #endif  
  179.     return 0;  
  180. }  
  181.   
  182. //输出  
  183. /* 
  184. //没有继承,有虚函数的情况 
  185. Base1::Base1Func1       m_iData=100     a=500   b=600 
  186. Base1::Base1Func2       m_iData=100     a=500   b=600 
  187.  
  188. //单继承 
  189. Base1::Base1Func1       m_iData=100     a=500   b=600 
  190. Base1::Base1Func2       m_iData=100     a=500   b=600 
  191. D1::D1Func      m_iData=200     a=500   b=600 
  192.  
  193. //多继承 
  194. Base1::Base1Func1       m_iData=100     a=500   b=600 
  195. Base1::Base1Func2       m_iData=100     a=500   b=600 
  196. D::DFunc        m_iData=300     a=500   b=600 
  197.  
  198. Base2::Base2Func1       m_iData=200     a=500   b=600 
  199. Base2::Base2Func2       m_iData=200     a=500   b=600 
  200.  
  201. //虚拟继承 
  202. VD1::VD1Func    m_iData=4294960 a=500   b=600 
  203.  
  204. Base1::Base1Func1       m_iData=100     a=500   b=600 
  205. Base1::Base1Func2       m_iData=100     a=500   b=600 
  206.  
  207. //棱形继承 
  208. Base1::Base1Func1       m_iData=100     a=500   b=600 
  209. Base1::Base1Func2       m_iData=100     a=500   b=600 
  210.  
  211. VD1::VD1Func    m_iData=4295032 a=500   b=600 
  212.  
  213. 请按任意键继续. . . 
  214. */  
0 0