一、问题引入
你在管理一堆动态分配的Widgets,每一个都可能通过检验,你把结果指针保存在一个vector中:
class Widget{
public:
...
// 这个Widget是否通过检验
bool isCertified() const;
...
};
// 建立一个vector然后用
vector<Widget*> v;
...
// 动态分配的Widget 的指针填充
v.push_back(new Widget);
当和v工作一段时间后,你决定除去未通过检验的Widget,因为你不再需要它们了。你自然会想到转向erase-remove惯用法,虽然这次你使用了remove_if:
// 删除未通过检验的 Widget指针
v.erase( remove_if(v.begin(), v.end(), not1(mem_fun(&Widget::isCertified))), v.end());
突然你开始担心erase的调用,因为你朦胧的记起关于摧毁容器中的一个指针也不会删除指针指向的东西。这是个合理的担心,但在这里,太晚了。当调用erase时,极可能你已经泄漏了资源。担心erase,是的,但首先,担心一下remove_if。
我们假设在调用remove_if前,v看起来像这样,我已经指出了未通过检验的Widget:
在调用remove_if后,一般来说v看起来像这样(包含从remove_if返回的迭代器):
如果你看不懂这个转换,请看前面关于remove的博客
一旦remove_if和erase返回后,情况看起来像这样:
这造成资源泄漏尤其明显了,现在你也很清楚为什么应该努力避免在动态分配的指针的容器上使用remove和类似算法(也就是,remove_if和unique)。在很多情况下,你会发现partition算法(请看前面关于STL部分排序的博客)是合理的替代品。
二、解决思路
1、如果你无法避免在那样的容器上使用remove,排除这个问题一种方法是在应用erase-remove惯用法之前先删除指针并设置它们为空,然后除去容器中的所有空指针:
// 如果*pWidget是一个未通过检验Widget, 删除指针并且设置它为空
void delAndNullifyUncertified(Widget*& pWidget) {
if (!pWidget->isCertified()) {
delete pWidget;
pWidget = 0;
}
}
// 把所有指向未通过检验Widget的指针删除并且设置为空
for_each(v.begin(), v.end(), delAndNullifyUncertified);
// 从v中除去空指针,0必须映射到一个指针,让C++可以正确地推出remove的第三个参数的类型
v.erase( remove(v.begin(), v.end(), static_cast<Widget*>(0)), v.end());
当然,这假设vector并不容纳任何你想保留的空指针。如果有的话,你可能必须自己写循环来按你的方式删除指针。
2、把指针的容器替换成执行引用计数的智能指针的容器,删除相关的困难就不存在了,你可以直接使用erase-remove惯用法
template<typename T>
// RCSP = “引用计数 智能指针”
class RCSP { ...};
// RCSPW = “RCSP to Widget”
typedef RCSP< Widget> RCSPW;
vector<RCSPW > v;
...
// 建立一个vector,用动态分配Widget的智能指针填充它
v.push_back(RCSPW(new Widget));
...
// erase未通过检验的Widget的指针没有资源泄漏
v.erase( remove_if(v.begin(), v.end(), not1 (mem_fun(&Widget::isCertified))), v.end());
如果在你的程序工具箱中碰巧没有一个引用计数智能指针模板,你应该从Boost库中得到shared_ptr模板 。