昨天问另外一个问题的时候, 今天早上有人回复给出了这样一段代码, 我试着运行了一下, 结果却大大出乎我的意料 :
class Father
{
public:
virtual void func1() { std::cout << "Father" << std::endl; }
virtual ~Father(){}
};
class Son :public Father
{
public:
virtual void func1(){ std::cout << "Son1" << std::endl; }
void func2(){ std::cout << "Son2" << std::endl; }
virtual ~Son(){}
};
class Factory
{
private:
Father* myFather;
public:
void setFun(Father* m_Father) { myFather = m_Father; }
Father* getFun() { return myFather;};
};
int main()
{
Father *m_Father = new Father();
Factory* m_Factory = new Factory();
m_Factory->setFun(m_Father );
m_Factory->getFun()->func1();
dynamic_cast<Son*>(m_Factory->getFun())->func2();
///
return 0;
}
运行结果是 :
Father
Son2
这让我感觉特别的奇怪 :
首先对于main
中的变量m_Father
, 他的静态类型和动态类型都是Father指针
, 之后使用dynamic_cast<Son*>
进行强制转换, 按照我的设想, 只有当m_Father
的动态类型, 也就是实际指向一个Son
的时候, 转换才能够成功, 然而这里不但成功转换, 同时还调用了Son
独有的函数fun2
, 这不是太奇怪了吗?
你試試在子類別裡加個資料成員然後在呼叫func時列印它。
你要理解在C++裡,類別的成員函數跟C裡面的函數沒什麼本質區別,只不過是記憶體裡的一段程式碼。你這裡的fun2也是一樣,當你把指標變成Son*型別時,你呼叫
(Son*)->func2()
,C++只會把它拼成Son$func2...
這樣的一個函式簽名,然後到符號表裡面去查這個函式簽名對應的記憶體位置,然後執行函數在程式中
dynamic_cast<Son*>(m_Factory->getFun())
並沒有轉換成功,是個空指針,但空指針上依然可以呼叫成員函數,因為能尋址成功類別的物件實例中,記憶體佈局中只存放成員變量,有virtual會再加一個vptr,類別的成員函數是單獨存放的。
如果一段程式碼裡的類別沒有任何成員變量,也不涉及虛函數,即使你用一個指向NULL的類別指標來調用,同樣會成功。這就類似於c語言呼叫函數。
一看就是被c++的語法糖毒害的患者,哈哈。對於這兩個類別其實本質上都繼承了father類別。本質都是包含了指向father的指標而已。強制轉換是取轉換物件為基準的並集。所以只取了son中的屬性作為轉換的屬性。而他兩個都繼承了father所以轉換是沒問題的