STL中的容器相當“聰明”,它們提供了迭代器,以便進行向後和向前的遍歷(通過begin、end、rbegin等);它們告訴你所包含的元素類型(通過它們的value_type類型定義);在插入和刪除的過程中,它們自己進行必要的內存管理;它們報告自己有多少對象,最多能容納多少對象(分別通過size和max_size);當然,當它們自身被析構時,它們自動析構所包含的每個對象。
有了這麼“聰明”的容器,許多程序員不再考慮自己做善後清理工作。更糟糕的是,他們認為,容器會考慮為他們做這些事情。很多情況下,他們是對的。但當容器包含的是new的方式而分配的指針時,他們這麼想就不正確了。沒錯,指針容器在自己被析構時會析構所包含的每個元素,但指針的“析構函數”不做任何事情!它當然也不會調用delete。
結果,下面的代碼直接導致資源洩露:
void doSomething() { vectorvwp; for (int I = 0; I < SOME_MAGIC_NUMBER; ++i) { Vwp.push_back(new Widget); … } //在這裡發生了Widget資源洩露 }
當vwp的作用域結束時,它的元素全部被析構,但並沒有改變通過new創建的對象沒有被刪除這一事實。刪除這些對象是你的責任,不是vector的責任。這是vector的特性。只有你才知道這些指針是否應該被釋放。
void doSomething() { for (vector::iterator I = vwp.begin(); i != vwp.end(); ++i) delete *i; }
這樣做能行,但只是在你對“能行“不那麼挑剔時。一個問題是,新的for循環做的事情和for_each相同,但不如使用for_each看起來那麼清楚。問題是,這段代碼不是異常安全的。如果在想vwp中填充指針和從中刪除指針的兩個過程中間有異常拋出的話,同樣會有資源洩露。
其一解決方法如下:
struct DeleteObject { templatevoid operator() (const T* ptr) const { delete ptr; } }; void doSomething() { deque dssp; for_each (dssp.begin(), dssp.end(), DeleteObject()); }
但它仍然不是異常安全的。如果在SpecialString已經被創建而對for_each的調用還沒開始時就有異常被拋出,則會有資源洩露發生。
其二解決方法:
利用Boost庫中的shared_ptr。
void doSomething() { typedef boost::shared_ptrSPW; //SPW= “指向Widget的shared_ptr” vector vwp; for (int I = 0; I < SOME_MAGIC_NUMBER; ++i) vwp.push_back(SPW(new Widget)); … } //這裡不會有Widget洩露,即使在上面的代碼中有異常被拋出
永遠不要錯誤的認為:你可以通過創建auto_ptr的容器使指針被自動刪除。這個想法很危險,具體解釋請看Effective STL 第8條。
你要記住的是:STL容器很智能,但沒有智能到知道是否該刪除自己所包含的指針的程度。當你使用指針的容器,而其中的指針應該被刪除時,為了避免資源洩露,你必須或者用引用計數形式的智能指針對象(比如Boost的shared_ptr)代替指針,或者容器被析構時手工刪除其中的每個指針。