在C++實現多態裏,有一個關於 析構函數的重寫問題:基類中的析構函數如果是虛函數,那麼派生類的析構函數就重寫了基類的析構函數。這裏他們的函數名不相同,看起來違背了重寫的規則,但實際上編譯器對析構函數的名稱做了特殊處理,編譯後析構函數的名稱統一處理成destructor。那麼爲什麼要把基類中的析構函數寫成虛函數呢?
原因:當基類指針指向派生類的時候,若基類析構函數不聲明爲虛函數,在析構時,只會調用基類而不會調用派生類的析構函數,從而導致內存泄露。
舉個例子:
- 當基類析構函數不是虛函數時:
class A {
public:
A() { cout << "A的構造" << endl; }
~A() { cout << "A的析構" << endl; }
void Work() {
cout << "A工作" << endl;
}
};//基類
class B :public A {
public:
B() { cout << "B的構造" << endl; }
~B() { cout << "B的析構" << endl; }
void Work() { cout << "B工作" << endl; }
}; //派生類
int main()
{
A* p = new B; //派生類對象賦給基類指針
p->Work();//此時調用的是基類的成員函數,因爲基類的成員函數覆蓋了派生類的同名成員函數
delete p;
system("pause");
return 0;
}
運行之:
可以看到在delete p的時候只調用了基類A的析構函數,並沒有調用派生類B的析構函數,導致內存釋放並不完全,出現內存泄漏的問題。
- 然後將基類析構函數寫爲虛函數時
class A {
public:
A() { cout << "A的構造" << endl; }
virtual ~A() { cout << "A的析構" << endl; }
void Work() {
cout << "A工作" << endl;
}
};//基類
class B :public A {
public:
B() { cout << "B的構造" << endl; }
~B() { cout << "B的析構" << endl; } //在派生類中重寫的成員函數可以不加virtual關鍵字
void Work() { cout << "B工作" << endl; }
};//派生類
int main()
{
A* p = new B;
p->Work();
delete p;
system("pause");
return 0;
}
運行之:
可以看到這次在delete p的時候調用了派生類的析構函數,因爲在調用派生類的析構函數後會自動調用基類的析構函數,這樣整個派生類的對象被完全釋放。
另外上面兩個過程中我們發現執行 "p->Work();" 時,也就是p在調用同名成員函數的時候,調用的始終是基類的成員函數,這是因爲基類的成員函數覆蓋了派生類的同名成員函數,如果想要調用派生類的成員函數,同樣將Work()設置爲虛函數即可。
class A {
public:
A() { cout << "A的構造" << endl; }
virtual ~A() { cout << "A的析構" << endl; }
virtual void Work() {
cout << "A工作" << endl;
}
};
class B :public A {
public:
B() { cout << "B的構造" << endl; }
~B() { cout << "B的析構" << endl; }
void Work() { cout << "B工作" << endl; }
};
int main()
{
A* p = new B; //派生類指針轉化成基類指針
p->Work();
delete p;
system("pause");
return 0;
}