首页 > 代码库 > Effective STL 第7条:如果容器中包含了通过new操作创建的指针,切记在容器对象析构前将指针delete掉
Effective STL 第7条:如果容器中包含了通过new操作创建的指针,切记在容器对象析构前将指针delete掉
STL中的容器相当“聪明”,它们提供了迭代器,以便进行向后和向前的遍历(通过begin、end、rbegin等);它们告诉你所包含的元素类型(通过它们的value_type类型定义);在插入和删除的过程中,它们自己进行必要的内存管理;它们报告自己有多少对象,最多能容纳多少对象(分别通过size和max_size);当然,当它们自身被析构时,它们自动析构所包含的每个对象。
有了这么“聪明”的容器,许多程序员不再考虑自己做善后清理工作。更糟糕的是,他们认为,容器会考虑为他们做这些事情。很多情况下,他们是对的。但当容器包含的是new的方式而分配的指针时,他们这么想就不正确了。没错,指针容器在自己被析构时会析构所包含的每个元素,但指针的“析构函数”不做任何事情!它当然也不会调用delete。
结果,下面的代码直接导致资源泄露:
void doSomething() { vector<Widget*>vwp; for (int I = 0; I < SOME_MAGIC_NUMBER; ++i) { Vwp.push_back(new Widget); … } //在这里发生了Widget资源泄露 }
当vwp的作用域结束时,它的元素全部被析构,但并没有改变通过new创建的对象没有被删除这一事实。删除这些对象是你的责任,不是vector的责任。这是vector的特性。只有你才知道这些指针是否应该被释放。
void doSomething() { for (vector<Widget*>::iterator I = vwp.begin(); i != vwp.end(); ++i) delete *i; }
这样做能行,但只是在你对“能行“不那么挑剔时。一个问题是,新的for循环做的事情和for_each相同,但不如使用for_each看起来那么清楚。问题是,这段代码不是异常安全的。如果在想vwp中填充指针和从中删除指针的两个过程中间有异常抛出的话,同样会有资源泄露。
其一解决方法如下:
struct DeleteObject { template<typename T> void operator() (const T* ptr) const { delete ptr; } }; void doSomething() { deque<SpecialString*> dssp; for_each (dssp.begin(), dssp.end(), DeleteObject()); }
但它仍然不是异常安全的。如果在SpecialString已经被创建而对for_each的调用还没开始时就有异常被抛出,则会有资源泄露发生。
其二解决方法:
利用Boost库中的shared_ptr。
void doSomething() { typedef boost::shared_ptr<Widget> SPW; //SPW= “指向Widget的shared_ptr” vector<SPW> 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)代替指针,或者容器被析构时手工删除其中的每个指针。
Effective STL 第7条:如果容器中包含了通过new操作创建的指针,切记在容器对象析构前将指针delete掉