class car { public: car(); ~car(); ... }; class diesel_car : public car {…}; class solar_car: public car {…}; class electric_car : public car {…};當客戶代碼中使用汽車對象時,如果他不關心使用的是具體哪一類汽車這個細節,那麼我們可以設計一個工廠函數(或者工廠類)負責創建一個汽車對象,該工廠函數返回一個base class指針或者引用,指向新生成的derived class對象:
car* get_car();
為遵守工廠函數的規矩,返回的對象必須位於heap(否則函數返回的指針在函數返回後將指向一個非法的位置,因為位於stack的對象的生命周期為函數域),因此為了避免內存洩漏,需要客戶代碼將工廠函數返回的對象適當地delete掉:
car* p_car = get_car(); // 從car繼承體系中獲得一個動態分配對象 … // 使用這個對象 delete p_car; // 釋放這個對象以避免內存洩漏
首先需要說明,上述做法已經存在兩個缺陷:
1.依賴客戶代碼執行delete操作,帶有錯誤傾向,客戶可能會忘記做這件事。 2.工廠函數結構應該考慮預防常見的客戶代碼錯誤。 但是最根本的弱點在於:客戶代碼根本無法將返回的derived class對象徹底銷毀。 簡單的做法便是:為base class定義一個virtual析構函數。此後刪除derived class對象就會銷毀這個對象,包括所有的derived class成分。class car { public: car(); virtual ~car(); ... };
class point { public: point(int coord_x, int coord_y); ~point(); private: int x, y; };32位系統中,int類型占32bits,因此point對象一共占64bits,可以被塞入一個64bit緩存器中,甚至可以被當作一個“64bit 量”傳給其他語言如C活著FORTRAN編寫的函數。 但是如果point內含析構函數時,point對象占用的空間將是96bits,(2個ints加1個vptr)。對象體積從64bits增加到96bits。 而在64bit計算機體系結構中,point對象將占用128bits(因為指針類型占用64bits)。對象體積從64bits增加到128bits。 這樣的對象將無法被塞入一個64-bit緩存器中,而C++的point對象也不再和其他語言(如C)內的相同聲明有著一樣的結構,因此也就無法將其傳遞到其他語言編寫的函數中,因此不再有移植性。 因此,將不用作多態用途的class的析構函數聲明為virtual是不合理的。只有當class內至少含有一個virtual函數時才應該將其析構函數聲明為virtual。
class abstract_class { public: virtual ~abstract_class() = 0; };但是要注意:必須為這個pure virtual析構函數提供一份定義:
abstract_class::~abstract_class(){}因為析構函數的運作方式是:最深層派生(most derived)的那個class的析構函數最先被調用,然後是其每一個base class的析構函數被調用。編譯器會在 abstract_class的derived classes中創建一個對~abstract_class的調用動作,所以必須為~abstract_class提供定義,否則鏈接器會報錯。