1.虛析構函數的作用:
當基類對象指針通過new動態創建一個子類的對象時,通過該指針釋放子類對象時,如果基類的析構函數不是虛函數,則釋放該對象時只會調用基類的析構函數而不會調用子類析構函數。這樣子類釋放對象時就無法釋放已分配的資源。
如果基類的析構函數爲虛函數,則在基類對象指針去釋放子類對象時,就會先調用子類的析構函數,在調用基類的析構函數:
#include<iostream>
using namespace std;
class A
{
public:
virtual void fun()
{
cout<<"thisis virtual for A"<<endl;
}
virtual~A()
{
cout<<"thisis virtual ~ for A"<<endl;
}
};
class B:public A
{
public:
voidfun()
{
cout<<"thisis virtual for B"<<endl;
}
virtual~B()
{
cout<<"thisis virtual ~ for B"<<endl;
}
};
int main()
{
A*a;
a=newB;
a->fun();
deletea;
return0;
}
結果:
this is virtual for B
this is virtual ~ for B
this is virtual ~ for A
當virtual ~A() 去掉virtual時,結果爲:
this is virtual for B
this is virtual ~ for A
也就是說,類B的析構函數根本就沒被調用,一般情況下類的析構函數裏面都是釋放內存資源,而析構函數不被調用的話就會造成內存泄漏。
所以基類的析構函數應該被定義爲虛函數,這樣做是爲了當用一個基類的指針刪除一個派生類的對象時,派生類的析構函數會被調用。
當然,並不是要把所有類的析構函數都寫成虛函數。因爲當類裏面有虛函數的時候,編譯器會給類添加一個虛函數表,裏面來存放虛函數指針,這樣就會增加類的存儲空間。所以,只有當一個類被用來作爲基類的時候,才把析構函數寫成虛函數。
2.虛函數表
多態性可分爲兩類:靜態多態和動態多態。函數重載和運算符重載實現的多態屬於靜態多態,動態多態性是通過虛函數實現的。
每個含有虛函數的類有一張虛函數表(vtbl),表中每一項是一個虛函數的地址, 也就是說,虛函數表的每一項是一個虛函數的指針。
沒有虛函數的C++類,是不會有虛函數表的
例子:
class Class1
{
public:
m_data1;
m_data2;
virtual vfunc1();
virtual vfunc2();
virtual vfunc3();
};
圖示:
class Class2 : public Class1
{
public:
m_data3;
virtual vfunc2();
};
圖示:
虛函數表的指針4個字節大小(vptr),存在於對象實例中最前面的位置(這是爲了保證取到虛函數表的有最高的性能——如果有多層繼承或是多重繼承的情況下)。這意味着我們通過對象實例的地址得到這張虛函數表,然後就可以遍歷其中函數指針,並調用相應的函數。