基類指針或引用可以指向或是引用派生類對象,下文以指針爲例作講解。引用的例子類似,不再贅述。
如果基類指針向派生類對象,則刪除此指針時,我們希望調用該指針指向的派生類析構函數,而派生類的析構函數又自動調用基類的析構函數,這樣整個派生類的對象完全被釋放。
若使用基類指針操作派生類,需要防止在析構時,只析構基類,而不析構派生類。
但是,如果析構函數不被聲明成虛函數,則編譯器採用的綁定方式是靜態綁定,在刪除基類指針時,只會調用基類析構函數,而不調用派生類析構函數,這樣就會導致基類指針指向的派生類對象析構不完全。若是將析構函數聲明爲虛函數,則可以解決此問題。
比較以下三個例子:
1、第一段代碼
#include<iostream>
using namespace std;
class BC{
public:
BC () { strsBC = new string[10]; cout << "BC::BC()!" << endl;};
~BC() {delete [] strsBC; cout << "free strsBC in BC::~BC()!" << endl;};
void fun1() { cout << "BC::fun1()!" << endl; };
private:
string * strsBC;
};
class DC : public BC{
public:
DC () {pa = new int[6]; cout << "DC::DC()!" << endl; };
~DC () {delete [] pa; cout << "free pa in DC::~DC()!" << endl; };
void fun1() { cout << "DC::fun1()!" << endl; };
private:
int *pa;
};
int main(){
DC *p = new DC();
p->fun1();
delete p;
return 0;
}
運行結果:
BC::BC()!
DC::DC()!
DC::fun1()!
free pa in DC::~DC()!
free strsBC in BC::~BC()!
這段代碼中,基類析構函數不是虛函數。在main函數中,用子類指針操作子類。釋放指針P的過程是:先釋放繼承類的資源,再釋放基類資源,則釋放是完全的,不存在問題。
2、第二段代碼
#include<iostream>
using namespace std;
class BC{
public:
BC () { strsBC =new string[10]; cout << "BC::BC()!" << endl;};
~BC() {delete [] strsBC; cout << "free strsBC in BC::~BC()!" << endl;};
void fun1() { cout << "BC::fun1()!" << endl; };
private:
string * strsBC;
};
class DC : public BC{
public:
DC () {pa = new int[6]; cout << "DC::DC()!" << endl; };
~DC () {delete [] pa; cout << "free pa in DC::~DC()!" << endl; };
void fun1() { cout << "DC::fun1()!" << endl; };
private:
int *pa;
};
int main(){
BC *p = new DC;
p->fun1();
delete p;
return 0;
}
輸出結果:
BC::BC()!
DC::DC()!
BC::fun1()!
free strsBC in BC::~BC()!
這段代碼中,基類析構函數不是虛函數,但在main函數中,用基類指針操作子類,釋放指針P的過程是:只是釋放了基類申請的資源,而沒有調用子類的析構函數,沒有釋放了子類申請的資源。調用fun1()函數,執行的也是基類定義的函數。
一般情況下,這樣的刪除只能夠刪除基類對象,而不能刪除子類對象,形成了刪除一半形象,造成內存泄漏。
要想解決上述問題,需要聲明析構函數是虛函數。
3、第三段代碼:
#include<iostream>
using namespace std;
class BC{
public:
BC () { strsBC =new string[10]; cout << "BC::BC()!" << endl;};
virtual ~BC() {delete [] strsBC; cout << "free strsBC in BC::~BC()!" << endl;};
virtual void fun1() { cout << "BC::fun1()!" << endl; };
private:
string * strsBC;
};
class DC : public BC{
public:
DC () {pa = new int[6]; cout << "DC::DC()!" << endl; };
~DC () {delete [] pa; cout << "free pa in DC::~DC()!" << endl; };
void fun1() { cout << "DC::fun1()!" << endl; };
private:
int *pa;
};
int main(){
BC *p = new DC;
p->fun1();
delete p;
return 0;
}
運行結果:
BC::BC()!
DC::DC()!
DC::fun1()!
free pa in DC::~DC()!
free strsBC in BC::~BC()!
這段代碼中,基類析構函數是虛函數。在main函數中,用基類指針操作子類,釋放指針P的過程是:先釋放子類申請的資源,再調用基類析構函數,釋放父類申請的資源。調用fun1()函數,執行的也是子類中定的函數,表現出了多態特徵。