倒立平行四邊形的虛擬繼承解析
結構:
A
| |
virtual virtual
| |
D D2
| |
TF
#include <iostream>
using namespace std;
class A
{
public:
A(int i=-1)
{
this->m_a = i;
}
virtual void fa()
{
cout<<"a::fa/n";
return ;
}
virtual void fa2()
{
cout<<"a::fa2/n";
}
virtual void fa3()
{
cout<<"a::fa3/n";
}
virtual void fa4()
{
cout<<"a::fa4/n";
}
protected:
int m_a;
};
class D: virtual public A //virtual
{
public:
virtual void fa()
{
cout<<"d::fa/n";
}
virtual void fa3()
{
cout<<"d::fa3/n";
}
virtual void fa4()
{
cout<<"d::fa4/n";
}
virtual void fd2()
{
cout<<"d::fd2/n";
}
virtual void fd3()
{
cout<<"d::fd3/n";
}
D(int a=1,int d=100):A(1)
{
this->m_d = 100;
}
protected:
int m_d;
};
class D2: virtual public A// virtual
{
public:
virtual void fa2()
{
cout<<"D2::fa2/n";
}
/* virtual void fa3()
{
cout<<"d2::fa3/n";
}
//如果D ,D2都給出實現,但是TF不給出fa3的實現的話就會編譯出錯,
*/
virtual void fa4()
{
cout<<"d2::fa4/n";
}
virtual void fd22()
{
cout<<"d2::fdd22/n";
}
D2(int a=2,int dd2=200):A(2)
{
this->m_dd2=200;
}
protected:
int m_dd2;
};
class TF:public D,public D2
{
public:
virtual void ftf()
{
cout<<"d2::fdd22/n";
}
virtual void ftf2()
{
cout<<"d2::fdd22/n";
}
virtual void fa4()//最高優先順序
{
cout<<"tf::fa4/n";
}
TF():D(),D2()
{
m_tf=300;
}
protected:
int m_tf;
};
當建立TF的對象時建構函式的執行順序是:
TF()函數頭-------〉完全執行A(),注意在執行這個函數時,調用的時無參的建構函式,或者是有預設值的建構函式,因為這是規則,在這種情形下,假定編譯器不這麼做,那麼他使用的參數應該是D的建構函式還是D2的建構函式的參數呢? 所以,他就什麼都不選。就使用無參建構函式,或有預設值的建構函式。
-------〉完全執行D(),
-------〉完全執行D2(),
------->TF()函數體。
TF對象的記憶體空間可以分為三部分:D部分,D2部分,TF本特有的部分,其中
D部分分為: A部分,D特有的部分
D2部分分未:A部分,D2特有的部分
但是D,D2 部分中的A部分是相同的(使用相同的記憶體快)。
//至於具體的記憶體布局方式我不清楚是怎麼處理(放置)A這個共同快的
//不過呢可以猜測應該是D,D2 中至少有一個是使用索引或者指標指向了這個塊
//如果是只有一個使用索引(指標)那麼可以斷定這個記憶體在另外一個裡面
//如果兩個都使用索引(指標)那麼可以斷定可能是在 TF對象的開頭或者結尾放置了這個記憶體塊
/*根據這種布局方式,你可以判斷在顯事使用dll時,dll將不能使用這種繼承方式,否則在 用戶端將無法訪問。當然如果你告訴用戶端的你的類(組件)的布局方式,那麼就可以用這種方式了。
但是在com中這種方式是不被允許的,因為你不會告訴用戶端你的組件的聲明(布局)方式,你只會告訴用戶端你的介面的布局(聲明)方式。
*/
/*很遺憾,上面的那一段討論是錯誤的,在C++的com中可以這麼做,但是由於com規定語言無關性,同時別的
語言沒有虛擬繼承這種概念,所以將無法在別的語言中實現,所以com不建議使用虛擬繼承。況且在com是沒有必要進行介面的多重繼承的。理由是 你可以把把介面的虛擬繼承拆分成不用虛擬繼承的方式。
*/
這裡有一個關鍵的問題 那就是A部分是怎麼構建的,或者說用什麼方法來決定這個部分的值。在vc下,根據我
觀察得到的原則是:
A的非待用資料成員的值由A無參建構函式,或有預設值的建構函式中的操作決定。在上面的例子中m_a=-1
至於A有四個虛成員函數,對應的vtable中的三項的值的確定方式如下:
最先這個四個項的值就是A的四個虛函數的地址值。
然後再執行D()時如果D重寫了A的虛函數則相應的項的值由D的重寫虛函數的地址值來確定,(fa)
然後再執行D2()時如果D2重寫了A的虛函數則相應的項的值由D2的重寫虛函數的地址值來確定,(fa2)
注意這裡就會出現一個問題也就是如果D,D2 都重寫了一個函數,但是這個函數沒有被TF重寫,那麼就會發生變異錯誤。告訴你存在模糊現象。(fa3)
最後在執行TF()函數體時,如果TF重寫了A的虛函數則相應的項由TF的重寫的虛函數的地址來確定。也就是TF擁有最高的優先順序!!(比如fa4)