這篇文章的出現,主要是回答一些朋友問題時,以及自己在寫程式時的小小心得。
-
出來要,遲早要還的…而且要還得乾乾淨淨
只要是你使用
new來要記憶體,一定要用delete把要來的記憶體歸還(同理,如果是用new[]要來的,就要用delete []來還)。但很多人都會犯一個錯誤,就是忘了把 class 中 data member 的部份在解構子(destructor)中清除乾淨,比方說下列這個例子:
class MyClass { public: MyClass(); ~MyClass(); ... private: OtherClass *pObj; ... };
在
MyClass中定義了一個 private data memberpObj,通常在建構子(constructor)會將它初始化,比方說:MyClass::MyClass() { pObj = new OtherClass(...); ... }
結果卻忘了在
MyClass::~MyClass()裡 delete 掉pObj,這樣即便你釋放了由 MyClass 生出的物件,但卻讓 pObj 讓你有了 memory leak 的危機…所以一定要注意 class 中解構子是不是真的把 pObj 都清乾淨了!不過,C++ 裡在物件的繼承卻有個很少人會注意到的特性,比方說下面有個例子:
class BaseClass { public: BaseClass(); // ctor ~BaseClass(); // dtor .... private: .... }; class ChildClass : public BaseClass { public: ChildClass(); // ctor ~ChildClass(); // dtor ... private: X *pObj; // ptr to some obj. ... };
這樣的類別繼承關係很常見,我們可能也會定義一個 factory function 來產生所有繼承自 BaseClass 類別的物件,比方說像這樣:
BaseClass* createObject() { // 很常見的 box BaseClass *pRet = new ChildClass(); ... return pRet; }
然後就會很自然地在 code 中這樣寫:
BaseClass *pMyObj; ... pMyObj = createObject(); ... delete pMyObj;
問題就來了,在
createObject這個 function 裡我們產生的BaseClass物件來自ChildClass,但是我們卻 delete 一個 BaseClass 指標所指到的東西,這樣的寫法會讓ChildClass::~ChildClass()這個解構子沒被呼叫到而無法清乾淨 ChildClass 裡的記憶體,不過這個問題還算好解決,只要把 BaseClass 中的解構子加上virtual的關鍵字就可,變成這樣:class BaseClass { public: BaseClass(); virtual ~BaseClass(); ... };
這樣當你在 delete 一個
BaseClass物件時,會因為BaseClass的解構子被定義成virtual,而透過一個 pointer 來找到繼承它的 class 來呼叫該 class 的解構子(在本例中就是呼叫ChildClass::~ChildClass()),如此才能順利地如你所願地把記憶體清乾淨。參考書目:Effective C++, 3/e
(待續…)
歷史上的今天
- Google "knol" 計劃,吸引更多人貢獻知識 - 2007
文章分類:
標籤:

