天天看點

從為什麼要将基類的析構函數定義為虛函數談起~~

 首先,做一個最簡單的實驗,在電腦上運作下面的代碼,将會産生運作錯誤,這或許會使你百思不得其解:

顯然,這裡出現了記憶體洩露。

記得剛開始學繼承的時候,似乎就被強行的記住 “基類的析構函數要是虛函數!!”,可是為什麼呢?

顯然,這與對象在記憶體中的布局方式有關。

在c++中,如果類中有虛函數,那麼它就會有一個虛函數表的指針__vfptr,在類對象最開始的記憶體資料中。之後是類中的成員變量的記憶體資料。

值得注意的是,無論繼承(單層)層次有多深,對象中都隻有一個虛函數指針。。

也就是說,如果derived2 繼承 derived ,最終的記憶體布局中也隻有一個虛函數指針。

那麼上述類在記憶體中的存在方式如下圖:

從為什麼要将基類的析構函數定義為虛函數談起~~

顯然,這可以很好的解釋 析構函數 是virtual的例子了,也就是說,如果析構函數不是virtual,那麼在

base *d = new derived();該d對象所指向的是derived的虛函數表(因為虛函數表在對象記憶體的起始位置),這樣便可以通過base的指針找到derived對象的析構函數,進而在

delete  d ; 的時候可以釋放derived對象,不會導緻記憶體洩露。

事實上,隻需要将繼承層次的最頂端的父類的析構函數設為虛函數,就可以避免上述問題,因為基類的析構函數是虛的,那麼它的子類的析構函數也是虛的。

拓展:如果在基類中還存在其他的虛函數,很簡單,直接将該虛函數存放在虛函數清單中,如果子類繼承了改虛函數,那麼隻需要将子類對象的虛函數表中的函數替換為子類定義的版本即可。沒有繼承的,則沿用父類的版本。