Effective C++讀書筆記之七:爲多態基類聲明virtual析構函數

Item 07: Declare destructors virtual in polymorphic base classes

在C++當中,如果一個derived對象經由一個base class指針被刪除,而該base class帶着一個non-virtual析構函數,其結構未有定義——實際執行時通常發生的是對象的derived成分沒被銷燬,造成一個詭異的“局部銷燬“現象。這會導致資源泄露、數據結構敗壞以及令你在調試器上浪費許多時間。

消除這個問題的做法很簡單:給base class一個virtual析構函數。那麼此後刪除derived class對象就會如你想要的那般。

當class不企圖被當做base class,令其析構函數爲virtual往往是個餿主意。

欲實現出virtual函數,對象必須攜帶某些信息,主要用來在運行期決定哪一個virtual函數該被調用。這份信息通常是由一個所謂vptr指針指出。vptr指向一個由函數指針構成的數組,稱爲vtbl;每一個帶有virtual函數的class都有一個相應的vtbl。當對象調用某一virtual函數,實際被調用的函數取決於該對象的vptr所指向的那個vtbl——編譯器在其中尋找適當的函數指針。請看下面這個例子:

 class Point
 {
public:
	Point(int xCoord,int yCoord);
	~Point();
private:
	int x,y;
 };

如果Point class內含virtual函數,其對象的體積會增加:在32-bit計算機體系結構中將佔用64bits(爲了存放兩個ints)至96bits(兩個ints加上virtual);在64-bit計算機體系結構中將佔用64~128bits。爲此,爲Point添加一個vptr會增加其對象大小達50%~100%!Point對象不再能夠塞入一個64-bit緩存器,而C++的Point對象也不再和其他語言(如C)內的相同聲明有着同樣的結構(因爲其他語言的對應物並沒有vptr),因此會破壞其移植性。

並非所有的base classes的設計目的都是爲了多態用途。例如標準的string和STL容器都不被設計作爲base classes使用,更別提多態了。

//雲風評註:析構函數的本質源於RAII的編程風格。RAII固然是一種合理的資源回收方案。在所有的資源中,以內存最爲特殊。內存不像別的資源那樣是以原子方式一個個地使用和回收的,而是從一整塊上一點點切分出去,在回收的時候又一塊塊歸還,並以不同的尺寸複用。內存的分配和回收本身就是個複雜的過程,這使得內存管理需要額外的數據空間和執行復雜的算法。許多軟件在正常關閉的時候會引起嚴重的硬盤顛簸就不難理解了。不再使用的內存引用由於管理內存本身的需要又要被訪問到,從硬盤的交換分區交換進來,僅僅爲了邏輯上釋放它們。諷刺的是,大多素對象的析構函數消耗了巨量的CPU時間,僅僅是爲了正確地調用其他對象的析構函數,而最終只是釋放它共有的一塊完整的內存空間。//

請記住:
1.帶多態性質的base classes應該聲明一個virtual析構函數。如果class帶有任何virtual函數,它就應該擁有一個virtual析構函數。
2.Classes的設計目的如果不是作爲base classes使用,或不是爲了具備多態性,就不該聲明virtual析構函數。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章