主要解釋強制類型轉換的影響。由於static_cast會在編譯期間檢測,dynamice_cast會在運行時檢測。
#include <iostream> #include <hash_map> using namespace std; class I1 { public: virtual void vf1() { cout << "I'm I1:vf1()" << endl; } }; class I2 { public: virtual void vf2() { cout << "I'm I2:vf2()" << endl; } }; class C : public I1, public I2 { private: hash_map<string, string> m_cache; }; I1* CreateC() { return new C(); } int main(int argc, char** argv) { I1* pI1 = CreateC(); pI1->vf1();
I2* pI2 = (I2*)pI1; pI2->vf2();
delete pI1; return 0; } 上述代碼執行結果以下:
先來看看前面代碼的內存佈局。 ios
之因此會出現pI1和pI2指向了同一個地址,是由於C++編譯器沒有足夠的知識來把IA*類型轉換爲IB*類型,只能按照傳統的C指針強制轉換處理,也就是指針位置不變。爲了驗證上面的結論,簡單的把pIA和pIB打印出來便可。把main()函數修改成以下: 算法
int main(int argc, char** argv) 函數
{ 佈局
I1* pI1 = CreateC(); spa
pI1->vf1(); .net
I2* pI2 = (I2*)pI1; 指針
pI2->vf2(); 對象
cout << "pI1指向的地址爲:"<<std::hex << pI1 << endl; blog
cout << "pI2指向的地址爲:"<<std::hex << pI2 << endl; 繼承
delete pI1;
return 0;
}
執行結果爲:
可見pI1和pI2確實指向了同一個地址,而這個地址就是I1類的虛表。因爲虛函數是按照順序定位的,編譯器編譯pI2->vf2()的時候,無論實際的pI2指向哪裏,都把它當作指向了I2的虛表,根據I2類定義,推出I2::vf2()這個函數位於其虛表的第0個位置,因此就直接把pI2指向的地址做爲vf2來調用。而實際上,這個位置偏偏是I1虛表的第0個位置,也就是I1::vf1的位置,因此實際執行時調用的是I1::vf1()。其實這種狀況是有些特殊的,也就是這個位置正好也是一個函數地址,並且函數原型也同樣,要是有任何不一樣的地方,就會形成調用失敗,反而更容易及時的提醒開發者。
強制類型轉換
static_cast:進行編譯期類型轉換,此時若是C++編譯期不能推算出指針調整算法,就會報錯,提醒開發者。
dynamic_cast:使用dynamic_cast進行運行期動態類型轉換,這須要開啓編譯器的RTTI。
reinterpret_cast:地址的轉換,不須要檢測類型。
在C++中,指針的類型轉換是常常發生的事情,好比將派生類指針轉換爲基類指針,將基類指針轉換爲派生類指針。指針的本質其實就是一個整數,用以記錄進程虛擬內存空間中的地址編號,而指針的類型決定了編譯器對其指向的內存空間的解釋方式。C++中對指針進行類型轉換,不會改變指針的值,只會改變指針的類型(即改變編譯器對該指針指向內存的解釋方式),可是這個結論在C++多重繼承下是 不成立的。
#include <iostream>
using namespace std;
class CBaseA
{
public:
char m_A[32];
};
class CBaseB
{
public:
char m_B[64];
};
class CDerive : public CBaseA, public CBaseB
{
public:
char m_D[128];
};
int main()
{
auto pD = new CDerive;
auto pA = (CBaseA *)pD;
auto pB = (CBaseB *)pD;
cout << pA << '\n' << pB << '\n' << pD << endl;
cout << (pD == pB) << endl;
}
這段代碼的輸出是:
0x9f1080
0x9f10a0
0x9f1080
1
pB與pD的指針差值正好是CBaseA佔用的內存大小32字節,而pA與pD都指向了同一段地址。這是由於,將一個派生類的指針轉換成某一個基類指針,編譯器會將指針的值偏移到該基類在對象內存中的起始位置。
輸出1表示pD和pB是相等的,而剛剛咱們才說明了,pD和pB的地址是相差了32個字節的。
其實這也是編譯器爲你們屏蔽了這種指針的差別,當編譯器發現一個指向派生類的指針和指向其某個基類的指針進行==運算時,會自動將指針作隱式類型提高已屏蔽多重繼承帶來的指針差別。由於兩個指針作比較,目的一般是判斷兩個指針是否指向了同一個內存對象實例,在上面的場景中,pD和pB雖然指針值不等,可是他們確確實實都指向了同一個內存對象(即new CDerive;產生的內存對象 ),因此編譯器又在此處插了一腳,讓咱們能夠安享==運算的上層語義。