深入理解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所在内存的内容,下面是该函数的实现:
- //PrintLayout.hxx
- #pragma once
- #include <iostream>
- #include <iomanip>
- #include <ReinterpretCast.hxx>
- template<typename T>
- void PrintLayout(T const & obj)
- {
- int * pObj = ReinterpretCast<int*>(&obj);
- for (int i =0; i<sizeof(obj)/sizeof(int);++i)
- {
- std::cout<<std::setw(10)<< pObj[i]<<std::endl;
- }
- }
接下来通过代码来分析一下在C++里,在没有继承,单继承,多继承以及虚继承等情况下对象的内存布局,下面是示例代码,为了减少代码量,我们将类的所有数据成员设为public的,在这里我们用struct来代替class:
- //main.cpp
- #include <iostream>
- #include <PrintLayout.hxx>
- #include <typeinfo>
- using namespace std;
- struct NoVirtualMemFunc
- {
- int Func1(int a,int b){
- cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;
- return 0;
- }
- int Func2(int a,int b){
- cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;
- return 0;
- }
- int m_iData;
- };
- struct Base1
- {
- virtual int Base1Func1(int a,int b){
- cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;
- return 0;
- }
- virtual int Base1Func2(int a,int b){
- cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;
- return 0;
- }
- int m_iData;
- };
- struct Base2
- {
- virtual int Base2Func1(int a,int b){
- cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;
- return 0;
- }
- virtual int Base2Func2(int a,int b){
- cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;
- return 0;
- }
- int m_iData;
- };
- struct D1:public Base1
- {
- virtual int D1Func(int a,int b){
- cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;
- return 0;
- }
- int m_iData;
- };
- struct D:public Base1,public Base2
- {
- virtual int DFunc(int a,int b){
- cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;
- return 0;
- }
- int m_iData;
- };
- struct VD1:public virtual Base1
- {
- virtual int VD1Func(int a,int b){
- cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;
- return 0;
- }
- int m_iData;
- };
- struct VD2:public virtual Base1
- {
- virtual int D2Func(int a,int b){
- cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;
- return 0;
- }
- int m_iData;
- };
- struct VD:public VD1,public VD2
- {
- int m_iData;
- };
- template<typename T>
- void PRINT_LAYOUT(T const & obj)
- {
- cout<<"The layout of "<<typeid(obj).name()<<"----------------"<<endl;
- PrintLayout(obj);
- cout<<endl;
- }
- int main(int argc, char* argv[])
- {
- //没有继承,没有虚函数的情况
- {
- NoVirtualMemFunc obj;
- obj.m_iData = 100;
- PRINT_LAYOUT(obj);
- }
- //没有继承,有虚函数的情况
- {
- Base1 obj;
- obj.m_iData = 100;
- PRINT_LAYOUT(obj);
- }
- //单继承
- {
- D1 obj;
- obj.Base1::m_iData = 100;
- obj.m_iData = 200;
- PRINT_LAYOUT(obj);
- }
- //多继承
- {
- D obj;
- obj.Base1::m_iData = 100;
- obj.Base2::m_iData = 200;
- obj.m_iData = 300;
- PRINT_LAYOUT(obj);
- }
- //虚拟继承
- {
- VD1 obj;
- obj.Base1::m_iData = 100;
- obj.m_iData = 200;
- PRINT_LAYOUT(obj);
- }
- //棱形继承
- {
- VD obj;
- obj.Base1::m_iData = 100;
- obj.VD1::m_iData = 200;
- obj.VD2::m_iData = 300;
- obj.m_iData = 500;
- PRINT_LAYOUT(obj);
- }
- return 0;
- }
- //输出
- /*
- The layout of struct NoVirtualMemFunc----------------
- 100
- The layout of struct Base1----------------
- 4294656
- 100
- The layout of struct D1----------------
- 4294740
- 100
- 200
- The layout of struct D----------------
- 4294800
- 100
- 4294776
- 200
- 300
- The layout of struct VD1----------------
- 4294876
- 4294888
- 200
- 4294864
- 100
- The layout of struct VD----------------
- 4294944
- 4294968
- 200
- 4294932
- 4294952
- 300
- 500
- 4294920
- 100
- 请按任意键继续. . .
- */
对于有虚表的函数,从上面的输出我们可以得到以下结论,
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
- //main.cpp
- #include <iostream>
- #include <GetVptr.hxx>
- #include <typeinfo>
- using namespace std;
- struct NoVirtualMemFunc
- {
- int Func1(int a,int b){
- cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;
- return 0;
- }
- int Func2(int a,int b){
- cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;
- return 0;
- }
- int m_iData;
- };
- struct Base1
- {
- virtual int Base1Func1(int a,int b){
- cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;
- return 0;
- }
- virtual int Base1Func2(int a,int b){
- cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;
- return 0;
- }
- int m_iData;
- };
- struct Base2
- {
- virtual int Base2Func1(int a,int b){
- cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;
- return 0;
- }
- virtual int Base2Func2(int a,int b){
- cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;
- return 0;
- }
- int m_iData;
- };
- struct D1:public Base1
- {
- virtual int D1Func(int a,int b){
- cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;
- return 0;
- }
- int m_iData;
- };
- struct D:public Base1,public Base2
- {
- virtual int DFunc(int a,int b){
- cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;
- return 0;
- }
- int m_iData;
- };
- struct VD1:public virtual Base1
- {
- virtual int VD1Func(int a,int b){
- cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;
- return 0;
- }
- int m_iData;
- };
- struct VD2:public virtual Base1
- {
- virtual int D2Func(int a,int b){
- cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;
- return 0;
- }
- int m_iData;
- };
- struct VD:public VD1,public VD2
- {
- int m_iData;
- };
- template<class T>
- struct MemFuncT
- {
- typedef int (T::* T_MemFuncT)(int,int);
- typedef int (T::* T_MemDataT);
- };
- template<class C>
- void CallMemFunc(int iFuncNum,int (C::**vptr)(int,int),C& obj,int a =500,int b =600)
- {
- for (int i =0;i<iFuncNum;++i)
- {
- //cout<<ReinterpretCast<void*>(vptr[i])<<" ";
- (obj.*vptr[i])(a,b);
- }
- cout<<endl;
- }
- int main(int argc, char* argv[])
- {
- //没有继承,有虚函数的情况
- {
- cout<<"//没有继承,有虚函数的情况"<<endl;
- Base1 obj;
- obj.m_iData = 100;
- MemFuncT<Base1>::T_MemFuncT * vptr = ReinterpretCast<MemFuncT<Base1>::T_MemFuncT *>(GetVptr(obj));
- CallMemFunc(2,vptr,obj);
- }
- //单继承
- {
- cout<<"//单继承"<<endl;
- D1 obj;
- obj.Base1::m_iData = 100;
- obj.m_iData = 200;
- MemFuncT<D1>::T_MemFuncT * vptr = ReinterpretCast<MemFuncT<D1>::T_MemFuncT *>(GetVptr(obj));
- CallMemFunc(3,vptr,obj);
- }
- //多继承
- {
- cout<<"//多继承"<<endl;
- D obj;
- obj.Base1::m_iData = 100;
- obj.Base2::m_iData = 200;
- obj.m_iData = 300;
- Base1 &objB1 = obj;
- MemFuncT<Base1>::T_MemFuncT * vptr = ReinterpretCast<MemFuncT<Base1>::T_MemFuncT *>(GetVptr(obj));
- CallMemFunc(3,vptr,objB1);
- Base2 &objB2 = obj;
- MemFuncT<Base2>::T_MemFuncT * vptrB2 = ReinterpretCast<MemFuncT<Base2>::T_MemFuncT *>(GetVptr(objB2));
- CallMemFunc(2,vptrB2,objB2);
- }
- #if 1
- //虚拟继承
- {
- cout<<"//虚拟继承"<<endl;
- VD1 obj;
- obj.Base1::m_iData = 100;
- obj.m_iData = 200;
- MemFuncT<VD1>::T_MemFuncT * vptr = ReinterpretCast<MemFuncT<VD1>::T_MemFuncT *>(GetVptr(obj));
- CallMemFunc(1,vptr,obj);
- Base1 & objB1 =obj ;
- MemFuncT<Base1>::T_MemFuncT * vptrB1 = ReinterpretCast<MemFuncT<Base1>::T_MemFuncT *>(GetVptr(objB1));
- CallMemFunc(2,vptrB1,objB1);
- }
- //棱形继承
- {
- cout<<"//棱形继承"<<endl;
- VD obj;
- obj.Base1::m_iData = 100;
- obj.VD1::m_iData = 200;
- obj.VD2::m_iData = 300;
- obj.m_iData = 500;
- Base1 & objB1 = obj;
- MemFuncT<Base1>::T_MemFuncT * vptrB1 = ReinterpretCast<MemFuncT<Base1>::T_MemFuncT *>(GetVptr(objB1));
- CallMemFunc(2,vptrB1,objB1);
- VD1 & objVD1 =obj;
- MemFuncT<VD1>::T_MemFuncT * vptrVD1 = ReinterpretCast<MemFuncT<VD1>::T_MemFuncT *>(GetVptr(objVD1));
- CallMemFunc(1,vptrVD1,objVD1);
- VD2 & objVD2 =obj;
- MemFuncT<VD2>::T_MemFuncT * vptrVD2 = ReinterpretCast<MemFuncT<VD2>::T_MemFuncT *>(GetVptr(objVD2));
- //CallMemFunc(1,vptrVD2,objVD2);
- }
- #endif
- return 0;
- }
- //输出
- /*
- //没有继承,有虚函数的情况
- Base1::Base1Func1 m_iData=100 a=500 b=600
- Base1::Base1Func2 m_iData=100 a=500 b=600
- //单继承
- Base1::Base1Func1 m_iData=100 a=500 b=600
- Base1::Base1Func2 m_iData=100 a=500 b=600
- D1::D1Func m_iData=200 a=500 b=600
- //多继承
- Base1::Base1Func1 m_iData=100 a=500 b=600
- Base1::Base1Func2 m_iData=100 a=500 b=600
- D::DFunc m_iData=300 a=500 b=600
- Base2::Base2Func1 m_iData=200 a=500 b=600
- Base2::Base2Func2 m_iData=200 a=500 b=600
- //虚拟继承
- VD1::VD1Func m_iData=4294960 a=500 b=600
- Base1::Base1Func1 m_iData=100 a=500 b=600
- Base1::Base1Func2 m_iData=100 a=500 b=600
- //棱形继承
- Base1::Base1Func1 m_iData=100 a=500 b=600
- Base1::Base1Func2 m_iData=100 a=500 b=600
- VD1::VD1Func m_iData=4295032 a=500 b=600
- 请按任意键继续. . .
- */
- 深入理解C++对象模型-对象的内存布局,vptr,vtable
- 深入理解C++对象模型-对象的内存布局,vptr,vtable
- 深入理解C++对象模型-对象的内存布局,vptr,vtable
- 《深入理解java虚拟机》读书笔记:Java对象的内存布局
- 深入理解Java虚拟机之对象的内存布局
- 《深入理解c++对象内存模型》读书笔记
- C++ 多重继承和虚拟继承的内存布局(vtable,vptr)
- 深入理解JVM02--深入理解对象的创建、内存布局、访问定位
- 深入理解C#的对象模型
- 深入理解JavaScript的对象模型
- 深入理解java虚拟机——对象内存布局
- c++对象模型 内存布局
- c++对象内存布局模型
- c++对象内存布局模型
- C++对象模型笔记:对象的三种内存布局
- C++对象模型笔记:对象的三种内存布局
- C++对象模型笔记:对象的三种内存布局
- C++对象模型之简述C++对象的内存布局
- BigInteger类的使用
- POJ 1163 最大树路径
- 十分精辟的人生格言
- map和set
- 树莓派系列三(max7219 led点阵)
- 深入理解C++对象模型-对象的内存布局,vptr,vtable
- 框架之Stucts2篇----day3.1
- chrome下,http请求重复问题
- 在Android的控件布局中,有一个奇葩的 layout_weight 属性
- POJ 1088 递归+记忆化搜索
- caffe训练GPU配置
- cookie是怎么被盗的
- 递归递推总结
- Number Sequence HDU