新手學C/C++:解構函式是否必須為虛擬函式?什麼情況下才應該定義解構函式為虛擬函式?
多型是面向物件的一個基本屬性,包括靜態多型(編譯階段)和動態多型(執行階段),靜態多型主要是指函式引數不同產生的多型性,是在編譯階段可以識別的一種多型機制,而執行時多型則主要用於基類指標指向派生類物件時,可以通過基類指標直接呼叫派生類的物件函式,當然這種多型是通過虛擬函式實現的。
虛擬函式的目的就是通知系統在函式呼叫時能夠自動識別對應的類物件型別,從而能夠根據指標所指型別呼叫對應的類物件,實現函式呼叫時的多型性。對於解構函式而言,同樣適用於上述規則。如果解構函式不是虛擬函式,那麼在呼叫該函式時(物件被刪除時)則只會呼叫當前物件對應的類的解構函式,這對於直接定義的物件是沒有什麼影響的,但是對於使用基類指向派生類的指標而言,因為基類指標實際上是基類型別,所以析構時自然只會呼叫基類的解構函式,這就可能產生記憶體洩漏(因為派生類的解構函式不被呼叫)。所以如果確定程式中有基類指標指向派生類的問題,則必須將基類的解構函式指定為虛擬函式,如此才能確保NEW出來的物件被正確的DELETE。
以下是幾個示例程式,用於方便理解:
class ClxBase{
public:
ClxBase() {};
~ClxBase() {cout << "Output from the destructor of class ClxBase!" << endl;};
void DoSomething() { cout << "Do something in class ClxBase!" << endl; };
};
class ClxDerived : public ClxBase{
public:
ClxDerived() {};
~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; };
void DoSomething() { cout << "Do something in class ClxDerived!" << endl; };
};
示例程式一:
int main(){
ClxBase base;
ClxDerived derived;
base.DoSomething();
derived.DoSomething();
return 0;
}
執行結果:
Do something in class ClxBase!
Do something in class ClxDerived!
Output from the destructor of class ClxDerived!
Output from the destructor of class ClxBase!
Output from the destructor of class ClxBase!
分析:
從以上例項程式可以發現,正常構造的物件,派生類的解構函式會主動呼叫基類的解構函式,所以不會存在記憶體洩漏問題。
示例程式二:
int main(){
ClxDerived *p = new ClxDerived;
p->DoSomething();
delete p;
return 0;
}
執行結果:
Do something in class ClxDerived!
Output from the destructor of class ClxDerived!
Output from the destructor of class ClxBase!
分析:
從以上結果可以發現,派生類的指標在析構時,同樣是執行指標所對應型別的派生類的解構函式,而此解構函式自然會呼叫基類的解構函式,所以也不會產生記憶體洩漏。
例項程式三:
int main(){
ClxBase *p = new ClxDerived;
p->DoSomething();
delete p;
return 0;
}
執行結果:
Do something in class ClxBase!
Output from the destructor of class ClxBase!
分析:
從以上結果可以發現,基類指標在析構時,會呼叫基類的解構函式,縱然其指向派生類,但其依然會呼叫基類的析構,所以派生類中如有需要釋放的記憶體空間,則必然得不到釋放,從而產生記憶體洩漏。
示例程式四:
class ClxBase{
public:
ClxBase() {};
virtual ~ClxBase() {cout << "Output from the destructor of class ClxBase!" << endl;};
virtual void DoSomething() { cout << "Do something in class ClxBase!" << endl; };
};
class ClxDerived : public ClxBase{
public:
ClxDerived() {};
~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; };
void DoSomething() { cout << "Do something in class ClxDerived!" << endl; };
};
int main(){
ClxBase *p = new ClxDerived;
p->DoSomething();
delete p;
return 0;
}
執行結果:
Do something in class ClxDerived!
Output from the destructor of class ClxDerived!
Output from the destructor of class ClxBase!
分析:
以上結果可以發現,不管是普通成員函式還是解構函式,只有定義為虛擬函式,才能夠在基類指標指向派生類物件時呼叫所指向物件的響應函式,從而在函式析構時能夠正確的delete掉new出來的物件,不產生記憶體洩漏。
來源: http://blog.csdn.net/zhangqk2016/article/details/51849535