虛析構(gòu)函數(shù)
#include <iostream>
#include<string>
#include<cstdlib>
using namespace std;
class A
{
public:
~A()
{
cout << "A destructor" << endl;
}
};
class B :public A
{
~B()
{
cout << "B destructor" << endl;
}
};
int main()
{
A *pa = new B;
delete pa;
return 0;
}
運(yùn)行結(jié)果:
A destructor // 只引發(fā)了 A 類的析構(gòu)函數(shù)被調(diào)用,沒有引發(fā) B 類的析構(gòu)函數(shù)
當(dāng)將基類指針指向 new 運(yùn)算符動(dòng)態(tài)生成的派生類對(duì)象時(shí),要通過 delete 來釋放,應(yīng)為該語句是靜態(tài)聯(lián)編的,編譯器不可能知道此時(shí) pa 指向哪個(gè)類型的對(duì)象,它只是根據(jù) pa 類型是 A * ,來決定調(diào)用 A 類的析構(gòu)函數(shù),但實(shí)際上應(yīng)該調(diào)用 B 類的析構(gòu)函數(shù)才符合邏輯。
綜上,delete pa 這樣的語句應(yīng)該根據(jù) pa 所指的對(duì)象,來執(zhí)行相應(yīng)的析構(gòu)函數(shù)。要實(shí)現(xiàn)這點(diǎn),也用到了多態(tài)。因此,需要將基類的析構(gòu)函數(shù)聲明為虛函數(shù),即虛析構(gòu)函數(shù)。
改為:
class A
{
virtual ~A()
{
cout << "A destructor" << endl;
}
}
運(yùn)行結(jié)果:
B destructor
A destructor
注意
派生類的析構(gòu)函數(shù)會(huì)自動(dòng)調(diào)用基類的析構(gòu)函數(shù)。
一般,如果一個(gè)類定義了虛函數(shù),則最好將析構(gòu)函數(shù)也定義成虛函數(shù)。
只要在基類中某個(gè)函數(shù)被聲明為虛函數(shù),那么在派生類中,同名,同參數(shù)表的成員函數(shù)即使前面不寫 virtual 關(guān)鍵字,也自動(dòng)成為虛函數(shù)。
編譯器看到是哪個(gè)類的指針,那么就會(huì)認(rèn)為通過它訪問的,就應(yīng)該是哪個(gè)類的成員,編譯器不會(huì)分析基類指針到底指向的是基類對(duì)象還是派生類對(duì)象。
#include <iostream>
using namespace std;
class A
{
public:
void fun()
{
cout << "A called" << endl;
}
};
class B :public A
{
void fun()
{
cout << "B called" << endl;
}
};
int main(void)
{
B b;
A *pa = &b;
pa->fun();
return 0;
}
運(yùn)行結(jié)果:
A called
如果用上多態(tài),則解決了這個(gè)問題:
#include <iostream>
using namespace std;
class A
{
public:
virtual void fun()
{
cout << "A called" << endl;
}
};
class B :public A
{
void fun()
{
cout << "B called" << endl;
}
};
int main(void)
{
B b;
A *pa = &b;
pa->fun();
return 0;
}
運(yùn)行結(jié)果:
B called
由上,只在基類 A 中的 fun 函數(shù)前加了關(guān)鍵字 virtual (line 120),是該函數(shù)及其派生類中的同名,同參數(shù)表中的成員函數(shù)自動(dòng)成為了虛函數(shù),通過基類的指針調(diào)用虛函數(shù)時(shí),就用到了多態(tài)。