问题实例
假设我们在管理一些动态分配的Widgets*:1
2
3
4
5
6
7
8class Widget{
public:
...
bool isCertified() const;//是否达标
...
};
vector<Widget*> v;
v.push_back(new Widget);//在容器中存放指针
最终需要删除所有没有通过检测的widget,所以下述写法似乎是十分自然的:1
2//关于谓词使用,详见Effective STL 41
v.erase(remove_if(v.begin(), v.end(),not1(mem_fun(&Widget::isCertified))), v.end());
Effective STL 7 告诉过我们删除容器内部的指针其实并没有析构对象本身,那是对的,但在这里内存泄漏在调用remove时就已经发生了。
问题剖析
假设在未调用remove_if之前,容器构成如下:
那么在调用之后,容器变成了这样:
其实在这里我们就可以清楚地看到,我们再也无法通过某个指针来获取BC了,原本指向它们的指针被覆盖,BC将永远无法释放。
在调用erase之后,容器变成了这样:
一般来说,在含有指针的容器中使用remove,remove_if或者unique不太合适。partition算法比较适合这类场景。(因为不存在覆盖的情况)
问题解决
依然erase_remove
我们可以先删除所有要删除的指针,并把它们设置为空,然后再抹除元素内所有空指针:1
2
3
4
5
6
7
8void delAndNullifyUncertified(Widget*& pWidget) {
if (!pWidget->isCertified()) {
delete pWidget;
pWidget = nullptr;
}
}
for_each(v.begin(), v.end(),delAndNullifyUncertified);
v.erase(remove(v.begin(), v.end(),[](Widget* pWidget){return pWidget;}),v.end());
智能指针
通过智能指针我们可以直接erase_remove:1
2
3
4typedef shared_ptr<Widget> SPW; // RCSPW = “RCSP to Widget”
vector<SPW> v;
v.push_back(RCSPW(new Widget));
v.erase(remove_if(v.begin(), v.end(),not1 (mem_fun(&Widget::isCertified))), v.end());
上述程序通过编译的前提是你的智能指针类型就必须可以隐式转换为相应的raw pointer。因为容器持有智能指针,但被调用的成员函数需要的是raw pointer。