假設我們有乙個基類a,很不幸的,a的析構函式是乙個non-virtual。同時我們有乙個派生類b,它派生自a。
我們定義了乙個a型別指標,它指向的實際物件是b:
a * ptr = new b;
然後在程式的某處,我們不再需要b了,我們將指標ptr delete掉:
delete ptr;
注意new永遠要和delete成隊出現,除非你使用智慧型指標。這時由於a的析構函式是non-virtual,乙個悲劇誕生:
c++中明確指出,當派生類物件經由乙個基類指標被刪除,而該基類帶著乙個non-virtual析構函式,其結果未定義--實際執行時通常發生的是物件的派生部分沒有被銷毀。也就是b物件部分發生記憶體洩漏。
問題的解決辦法就是為多型基類a宣告乙個虛析構函式。簡單吧,另乙個更加通用的準則是:如果乙個類擁有至少乙個虛函式,那麼這個類也應該要有乙個虛析構函式,因為類中擁有虛函式已經表明該類將作為基類來使用,期待子類實現自己的虛函式,此時基類應該為析構函式作virtual宣告。
反過來的另一條準則是,如果乙個類不想作為基類,那麼不要為其宣告任何成員函式做virtual宣告。為啥?virtual有代價啊。為了支援多型機制,編譯器為類物件安插乙個虛表指標,同時類也多了一張虛函式表。如果我們的類本來只有乙個int型別的資料成員,在32bit機器上,sizeof(類)為4,而加了vptr後,sizeof(類)為8,類物件的大小整整翻了100%!
當然,就算我們的程式設計遵循了上面的準則,有時候還有有些許錯誤。切勿不要以沒有任何虛函式的類作為基類。有時候由於我們的粗心大意或者在毫不知情的情況下我們可能會幹這種事情。例如,我們想實現乙個自己的容器mylist,並想借助stl的list來實現:
class mylist :public list
;
那麼記憶體洩漏的問題又來了,stl中的list並不打算作為乙個基類來使用,因此它的析構函式也不是乙個virtual。類似的還有string、stl的vector、set、unordered_map等等。
好在,c++11 提供了final關鍵字,對於不想被繼承的類,我們在其定義時加上final關鍵字:
class super final
;
這樣試圖繼承super時編譯器將會報錯。
基類虛析構函式
why虛析構函式?主要還是由於基類指標或引用可以不進行顯示型別轉換的情況下指向派生類物件。同時通過引用或者指標或引用呼叫方法時遵循 例如 定義了基類brass,派生類brassplus 如果viewacct 不是虛方法,則b1 ref.viewacct 和b2 ref.viewacct 均是使用br...
多型基類的析構函式應該為虛函式
includeusing namespace std class cbird cbird virtual void fly class clark public cbird clark void fly int main cbird作為基類描述鳥類的一般行為和屬性,因為不同鳥類的飛行特點不同,所以基...
多型基類的析構函式應該為虛函式
includeusing namespace std class cbird cbird virtual void fly class clark public cbird clark void fly int main cbird作為基類描述鳥類的一般行為和屬性,因為不同鳥類的飛行特點不同,所以基...