Effective C++筆記:為多態(tài)基類聲明virtual析構函數(shù)
就像本文標題所說的那樣,應該為多態(tài)基類聲明virtual析構函數(shù),否則容易造成內存泄露。?因為C++明白指出,當derived class對象經(jīng)由一個base class指針被刪除,而該base class如果帶有一個non-virtual析構函數(shù),其結果未定義一實際執(zhí)行時通常發(fā)生的是對象的derived成分沒被銷毀。舉個例子:
#includeusing?namespace?std;
class?Base
{
public:
????Base(){?cout?<<?"base?構造"?<<?endl;?}
????//??virtual?~Base(){cout<<"base?析構"<<endl;}??
?????~Base(){?cout?<<?"base?析構"?<<?endl;?}
????virtual?void?fun(){?cout?<<?"base?fun"?<<?endl;?}
private:
};
class?Derived?:public?Base
{
public:
????Derived(){?cout?<<?"drived?構造"?<<?endl;?}
????~Derived(){?cout?<<?"drived?析構"?<<?endl;?}
????void?fun(){?cout?<<?"drived?fun"?<<?endl;?}
};
int?main()
{
????Base?*pd?=?new?Derived();
????pd->fun();//打印drived?fun,實現(xiàn)多態(tài)
????delete?pd;
????system("pause");
}? ? ? ?這個程序的運行結果是:
? ? ? ?并沒有調用drived class的析構函數(shù),然而其base class成分通常會被銷毀,于是造成一個詭異的"局部銷毀"對象,從而造成內存泄露 。
? ? ? ?如果將基類的析構函數(shù)聲明為virtual,那么就OK了!?
? ? ? ?相反地,如果這個類并不是基類,那么不要將其析構函數(shù)聲明為virtual函數(shù)。原因與virtual函數(shù)的實現(xiàn)機制:虛函數(shù)表有關。簡單的說,如果要實現(xiàn)虛函數(shù),那么這個對象就會附帶一個虛函數(shù)表指針指向一個由函數(shù)指針組成的數(shù)組,這個數(shù)組就是虛函數(shù)表,當對象使用虛函數(shù)時,需要從這張表中尋找適當?shù)暮瘮?shù)指針,這大大的增加了開銷。
? ? ? ?我們還容易犯這樣的錯誤:繼承標準庫中的某個類。但是,標準庫中的很多類是不含virtual函數(shù)的!比如string,STL的容器等等。所以如果你這樣寫:?
class?MyString:public?string??
{??
public:??
????MyString(string?str,int?i):s(str),length(i){}??
????~MyString(){cout<<"MyString?析構函數(shù)"<<endl;}??
private:??
????string?s;??
????int?length;??
};若如此調用時就會造成和上面類似的風險:
string*?pStr?=?new?MyString("aaa",1);?
delete?pStr;? ? ? ?總之,如果這這個基類在派生過程中要實現(xiàn)多態(tài),那么就需要把它的析構函數(shù)聲明為virtual;如果這個類并不是用作基類或者并不是實現(xiàn)多態(tài),那么就不要把它的析構函數(shù)聲明為virtual。





