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----------------100The layout of struct Base1----------------4294656100The layout of struct D1----------------4294740100200The layout of struct D----------------42948001004294776200300The layout of struct VD1----------------429487642948882004294864100The layout of struct VD----------------42949444294968200429493242949523005004294920100請按任意鍵繼續. . .
對于有虛表的函數,從上面的輸出我們可以得到以下結論,
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);}
#endifreturn 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=600Base2::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=600Base1::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=600VD1::VD1Func m_iData=4295032 a=500 b=600請按任意鍵繼續. . .
*/