在c++中,指標的型別轉換是經常發生的事情,比如將派生類指標轉換為基類指標,將基類指標轉換為派生類指標。指標的本質其實就是乙個整數,用以記錄程序虛擬記憶體空間中的位址編號,而指標的型別決定了編譯器對其指向的記憶體空間的解釋方式。
基於上面的理解,我們似乎可以得出乙個結論,c++中對指標進行型別轉換,不會改變指標的值,只會改變指標的型別(即改變編譯器對該指標指向記憶體的解釋方式),但是這個結論在c++多重繼承下是 不成立的。
看下面一段**:
1 #include 2這段**的輸出是:using
namespace
std;34
class
cbasea5;
910class
cbaseb11;
1516
class cderive : public cbasea, public
cbaseb17;
2122
23int
main()
24
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的類宣告就可以統一決策的事情。
cout << (pd == pb) << endl;
輸出1表示pd和pb是相等的,而剛剛我們才說明了,pd和pb的位址是相差了32個位元組的。
其實這也是編譯器為大家遮蔽了這種指標的差異,當編譯器發現乙個指向派生類的指標和指向其某個基類的指標進行==運算時,會自動將指標做隱式型別提公升已遮蔽多重繼承帶來的指標差異。因為兩個指標做比較,目的通常是判斷兩個指標是否指向了同乙個記憶體物件例項,在上面的場景中,pd和pb雖然指標值不等,但是他們確確實實都指向了同乙個記憶體物件(即new cderive;產生的記憶體物件 ),所以編譯器又在此處插了一腳,讓我們可以安享==運算的上層語義。
from:
C 多重繼承下的指標型別轉換
在c 中,指標的型別轉換是經常發生的事情,比如將派生類指標轉換為基類指標,將基類指標轉換為派生類指標。指標的本質其實就是乙個整數,用以記錄程序虛擬記憶體空間中的位址編號,而指標的型別決定了編譯器對其指向的記憶體空間的解釋方式。基於上面的理解,我們似乎可以得出乙個結論,c 中對指標進行型別轉換,不會改...
C 多重繼承下的指標型別轉換
在c 中,指標的型別轉換是經常發生的事情,比如將派生類指標轉換為基類指標,將基類指標轉換為派生類指標。指標的本質其實就是乙個整數,用以記錄程序虛擬記憶體空間中的位址編號,而指標的型別決定了編譯器對其指向的記憶體空間的解釋方式。基於上面的理解,我們似乎可以得出乙個結論,c 中對指標進行型別轉換,不會改...
C 多重繼承與void 指標轉換問題
c 支援多重繼承,然而多重繼承可能會導致一些奇怪的問題,我前段時間遇到乙個指標轉換問題,非常典型。先看乙個簡單的測試 include using namespace std class ia virtual void a 0 class ib virtual void b 0 class cmult...