一、引入
- 如果通過一個基類指針申請一個派生類對象,那麼在通過這個指針釋放對象的時候,要求基類的虛函數是virtual的 。
二、虛析構函數
- 使用方法和規則與虛函數一樣
- 格式要求:
- 虛析構函數要求基類與派生類中的名稱不一緻
- 隻要基類的析構函數是虛函數,就能確定我們在釋放指針時準确的運作哪個版本(基類or派生類)的析構函數
- 如果基類指針指向于自己,那麼delete的時候執行的就是自己的析構函數
- 如果基類指針指向于派生類對象,那麼delete的時候執行的就是派生類的析構函數(這個就是多态的性質,與執行虛函數的原理一樣)
- 如果基類的析構函數不是虛函數,則delete一個指向派生類對象的基類指針将産生未定義的行為
- 與虛函數一樣,如果基類的析構函數為virtual,那麼派生類的析構函數也都是virtual的(即使是編譯器預設合成的也是virtual的)
三、虛析構函數的其它注意事項
- ①前面我們介紹過如果一個類需要析構函數,那麼它同樣需要拷貝和指派操作。但是基類的虛析構函數并不遵循這個規則:一個基類總是需要析構函數,而且它能将析構函數設定為虛函數,此時,該析構函數為了成為虛函數而令内容為空,我們顯然無法由此推斷該基類還釋放需要複制運算符或拷貝構造函數
- ②虛析構函數将阻止合成移動操作:基類需要一個虛析構函數這一事實還會對基類和派生類的定義産生另外一個間接的影響:如果一個類定義了析構函數,即使它通過default的形式使用了合成的版本,編譯器也不會為這個類合成一定操作(見合成移動操作(待續))
四、示範案例
#include <string>
#include <iostream>
using namespace std;
class A {
private:
int a;
public:
A() {
cout << "A()" << endl;
}
~A() {
cout << "~A()" << endl;
}
};
class B :public A {
public:
int b;
B(){
cout << "B()" << endl;
}
~B() {
cout << "~B()" << endl;
}
};
int main() {
A* pa = new B();
delete pa;
int a = 0;
}
輸出:
A()
B()
~A()
如果我們把A的析構函數聲明成虛拟函數
#include <string>
#include <iostream>
using namespace std;
class A {
private:
int a;
public:
A() {
cout << "A()" << endl;
}
virtual ~A() {
cout << "~A()" << endl;
}
};
class B :public A {
public:
int b;
B(){
cout << "B()" << endl;
}
~B() {
cout << "~B()" << endl;
}
};
int main() {
A* pa = new B();
delete pa;
int a = 0;
}
這樣就會得到我們想要的結果:
A()
B()