- 代碼初覺
先看一段代碼,想象它的結果是怎樣的?
delete p;
p = NULL;
p->DoSomething();
這樣的代碼不會拋出異常或導致崩潰嗎?對象都已經被delete掉而且都設爲空了,怎麼還可以調用其函數?
- 詭異的非虛函數
但對C++而言,這樣的代碼是有可能調用成功且不會產生任何異常的。但有一個條件,那就是這個函數必須是非虛函數。
示例代碼如下:
class TestClass
{
public:
void Go()
{
cout<<"Go OK!"<<endl;
}
};
int main()
{
TestClass* p = new TestClass();
delete p;
p = NULL;
p->Go();
return 0;
}
輸出爲:
原因:
該函數的調用地址在編譯期間就已經確定,而且非虛函數的地址只跟類定義本身有關,與具體的實例對象無關,所以當對象的內存會銷燬掉,並不會影響到對其非虛函數的調用。所以此次函數的調用可以成功。當然,如果該函數內部調用了對象的數據成員,還是會發生崩潰的。
- 不好惹的虛函數
那再來看見虛函數,還會不會在delete之後被調用得到呢?
class TestClass
{
public:
virtual void Go()
{
cout<<"Go OK!"<<endl;
}
};
int main()
{
TestClass* p = new TestClass();
delete p;
p = NULL;
p->Go();
return 0;
}
運行結果:崩潰
其原因就在於虛函數是延遲綁定的,不是在編譯期間就確定下來地址的,而是跟對象息息相關的。C++的內存模型裏就可以看出,虛函數是通過虛表指針來引用的,而虛表指針式在對象的前4個字節裏面。那麼當對象被銷燬時,其虛表指針就被銷燬,虛函數自然是無從引用了。
- 引用成員變量,更是別想了
class TestClass
{
public:
int m_data;
};
int main()
{
TestClass* p = new TestClass();
delete p;
p = NULL;
cout<<p->m_data; // crash!!!!
return 0;
}