C++:基類析構函數爲什麼要定義爲虛函數

        在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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章