C++虚继承(十) --- 谈谈陈皓遗留的问题
網(wǎng)上關(guān)于c++對象布局的文章挺多,而且《深度探索c++對象模型》(Inside The C++ Object Model?侯捷?譯)一書中也很詳細(xì)地介紹。如果你一點都不了解C++對象的布局,我推薦你看看《深度探索c++對象模型》的第三章,如果你意猶未盡下面的兩個系列都很不錯:
一是陳皓的《C++?對象的內(nèi)存布局》圖文并貌,寫得很是詳細(xì)。地址是http://blog.csdn.net/haoel/archive/2008/10/15/3081328.aspx。
二是玄機逸士的《對象內(nèi)存布局》系列則幾乎把每種可能性列出來了,尤為詳盡。地址是http://blog.csdn.net/pathuang68/archive/2009/04/23/4101970.aspx。
讀了以上好文,對于c++對象的布局其實應(yīng)該是山水了然于胸了,不過我最在寫一個用c++模仿C#事件機制的東東,但發(fā)覺得有很幾處細(xì)節(jié)不是很明了,而且陳皓朋友也在最后提出了一個問題,雖然有網(wǎng)友答之,仍語言不詳。而我卻必需深入了解此,才能真正實做出C++的事件。
此文應(yīng)該算是狗尾續(xù)貂之作,高手可能會不屑一顧。不過希望還是對一些朋友有幫助。因為對于普通類的對象布局,前人備述已,我也不太可能寫出什么新意來,更多的是因為我比較懶。呵呵,本文所探討的就是那種極端復(fù)雜的菱形結(jié)構(gòu)如下:
class A ;
class B : virtual public A;
class C : virtual public B;
class D : public B,public C;
?
??? 好了,來了,我們從最基礎(chǔ)的的討論起。當(dāng)c++支持virtual base class?時,就會多了一些額外負(fù)擔(dān),當(dāng)class中內(nèi)含一個或多個virtual base class subobject時,將分成兩個部分,一個不變局部和一個共享局部。最初的方案是為每一個虛基類安插一個指針指向這個虛基類,其缺點是為了負(fù)擔(dān)太重,而且當(dāng)虛繼承鏈加長時,導(dǎo)致間接存取時間加長(需通過多次跳轉(zhuǎn))。因此有兩種解決方案(《深入》一書中所提):
一、是引入virtual base class table,不管多少個虛基類,總是只有一個指針指向它,這個virtual base class table(VBTBL)包括真正的?virtual base class?指針。
二、Bjarne的辦法是在virtual function table中放置virtual base class的offset,而非地址,這個offset在virtual function table?的負(fù)位置(正值是索引virtual function,而負(fù)值則方向盤引到virtual base class offsets)。
我用vc2003觀測到的實際情況是。在類中增加一個指針(VBPTR)指向一個VBTBL,這個VBTBL的第一項記載的是從VBPTR?與本類的偏移地址,如果本類有虛函數(shù),那么第一項是FF FF FF FC(也就是-4),如果沒有則是零,第二項起是VBPTR與本類的虛基類的偏移值。vc2003的這種方案個人覺得沒有Bjarne的好,一是要多一個指針,二是因為VBPTR與虛函數(shù)表分開設(shè)計,也不便于修改。至于其它編譯器,因為我跟其它編譯器不熟,所以也就沒有實測它們。
下面給出對于類定義
struct B1
{
????int a;
????int b;
};
struct B2
{
????virtual void foo(void);
????int c;
????int d;
};
?
struct Test : virtual public B1, virtual public B2
{
????virtual void func1(void);
????virtual void func2(void);
????virtual void func3(void);
????int X;
};
一個Test?對象的內(nèi)存布局圖,我們可以清楚的看到在VS2003中VBPTR以及VBTBL的結(jié)構(gòu)以及其相關(guān)的內(nèi)容是什么意義。以及Bjarne的方案的優(yōu)點。
?
?
最后我們來看一個完整的例子以及內(nèi)存結(jié)構(gòu)布局。圖后有相關(guān)代碼。
?
?
代碼如下:
struct?A
{
????A(int?v=100):X(v){};
????virtual?void?foo(void){}
????int?X;
};
?
struct?B :virtual?public?A
{
????B(int?v=10):Y(v),A(100){};
????virtual?void?fooB(void){}
????int?Y;
};
?
struct?C :?virtual?public?A
{
????C(int?v=20):Z(v),A(100){}
????virtual?void?fooC(void){}
????int?Z;
};
?
?
struct?D :?public?B,?public?C
{
????D(int?v =40):B(10),C(20),A(100),L(v){}
????virtual?void?fooD(void){}
????int?L;
};
?
?
int?_tmain(int?argc, _TCHAR* argv[])
{
???
????A a;
????int?*ptr;
????ptr = (int*)&a;
????cout << ptr << " sizeof = " <<?sizeof(a) <<endl;
????for(int?i=0;i<sizeof(A)/sizeof(int);i++)
????{
????????if(ptr[i] < 10000)
????????{
?????????????cout << dec << ptr[i]<<endl;
????????}
????????else?cout << hex << ptr[i] <<" = " << hex << * ((int*)(ptr[i])) <<endl;
????}
?
????cout << "--------------------------------------" <<endl;
?
????B b;
????ptr = (int*)&b;
????cout <<"addr:" << ptr << " sizeof = " <<?sizeof(b) <<endl;
????for(int?i=0;i<sizeof(B)/sizeof(int);i++)
????{
????????if(ptr[i] < 10000)
????????{
?????????????cout << dec << ptr[i]<<endl;
????????}
????????else?cout << hex << ptr[i] <<" = " << hex << * ((int*)(ptr[i])) <<endl;
????}
?
????cout << "--------------------------------------" <<endl;
???
????D d;
????ptr = (int*)&d;
????cout <<"addr:" << ptr << " sizeof = " <<?sizeof(d) <<endl;
????for(int?i=0;i<sizeof(D)/sizeof(int);i++)
????{
????????if(ptr[i] < 10000)
????????{
?????????????cout << dec << ptr[i]<<endl;
????????}
????????else?cout << hex << ptr[i] <<" = " << hex << * ((int*)(ptr[i])) <<endl;
????}
????return?0;
}
總結(jié)
以上是生活随笔為你收集整理的C++虚继承(十) --- 谈谈陈皓遗留的问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++虚继承(九) --- 构造函数调用
- 下一篇: 在VC中如何找到崩溃的源头