在C++中,指標的類型轉換是經常發生的事情,比如將衍生類別指標轉換為基類指標,將基類指標轉換為衍生類別指標。指標的本質其實就是一個整數,用以記錄進程虛擬記憶體空間中的地址編號,而指標的類型決定了編譯器對其指向的記憶體空間的解釋方式。
基於上面的理解,我們似乎可以得出一個結論,C++中對指標進行類型轉換,不會改變指標的值,只會改變指標的類型(即改變編譯器對該指標指向記憶體的解釋方式),但是這個結論在C++多重繼承下是 不成立的。
看下面一段代碼:
1 #include <iostream> 2 using namespace std; 3 4 class CBaseA 5 { 6 public: 7 char m_A[32]; 8 }; 9 10 class CBaseB11 {12 public:13 char m_B[64];14 };15 16 class CDerive : public CBaseA, public CBaseB17 {18 public:19 char m_D[128];20 };21 22 23 int main()24 {25 auto pD = new CDerive;26 auto pA = (CBaseA *)pD;27 auto pB = (CBaseB *)pD;28 29 cout << pA << '\n' << pB << '\n' << pD << endl;30 cout << (pD == pB) << endl;31 }
這段代碼的輸出是:
0x9f1080
0x9f10a0
0x9f1080
1
可以看出,指向同一個堆上new出來的記憶體指標,在經過類型轉換之後,其值會發生改變。究其原因,要從C++中多重繼承的記憶體布局說起。
new CDerive;執行之後,產生的記憶體布局如下:
同時我們注意到,pB與pD的指標差值正好是CBaseA佔用的記憶體大小32位元組,而pA與pD都指向了同一段地址。這是因為,將一個衍生類別的指標轉換成某一個基類指標,編譯器會將指標的值位移到該基類在對象記憶體中的起始位置。
可是為什麼C++要這樣設計呢?
試想,沿用上面的情境,如果pB和pA都指向對象的首地址,那麼使用pB指標來定位CBaseB的成員變數m_B時,編譯器應該將pB指標位移32個位元組,從而跳過CBaseA的記憶體部分。而pB指標如果是這樣產生的auto pB = new CBaseB;,那麼使用pB指標來定位CBaseB的成員變數m_B時,位移量應該為0。
關鍵在於對於一個指標而言,編譯器不關心也無法知道該指標的來源(一種極端情況,指標是從其他模組傳遞過來的),而只是把它視為一個有指標類型的整數。所以對於CBaseB類型的指標,取CBaseB的成員變數m_B時,位移量應該通通為0,這是通過CBaseB的類聲明就可以統一決策的事情。
說到這裡,pD和pB的指標地址為什麼不一樣大家應該清楚了,可是為什麼下面的代碼輸出是1呢?
cout << (pD == pB) << endl;
輸出1表示pD和pB是相等的,而剛剛我們才說明了,pD和pB的地址是相差了32個位元組的。
其實這也是編譯器為大家屏蔽了這種指標的差異,當編譯器發現一個指向衍生類別的指標和指向其某個基類的指標進行==運算時,會自動將指標做隱式型別提升已屏蔽多重繼承帶來的指標差異。因為兩個指標做比較,目的通常是判斷兩個指標是否指向了同一個記憶體對象執行個體,在上面的情境中,pD和pB雖然指標值不等,但是他們確確實實都指向了同一個記憶體對象(即new CDerive;產生的記憶體對象 ),所以編譯器又在此處插了一腳,讓我們可以安享==運算的上層語義。
from:http://www.cnblogs.com/itZhy/archive/2012/10/08/2713367.html