假如现在有一些动态分配的Widget,其中每一个Widget可能已经被验证过了,然后把结果指针存放在一个vector中:
class Widget {
public:
// 是否被验证过
bool isCertified() const;
...
};
vector<Widget*> v;
...
v.push_back(new Widget);
你想要删除那些没被验证过的Widget,基于第32条,你会自然地使用erase-remove_if习惯用法。
// 删除那些指向未被验证过的Widget对象的指针
// mem_fun见第41条
v.erase(remove_if(v.begin(), v.end(), not1(mem_fun(&Widget::isCertified))), v.end());
基于第7条,删除容器中的裸指针会导致资源泄漏(没delete)。但实际上,调用remove_if之后就已经造成资源泄漏了。
在调用remove_if之前v的布局如下图所示:
todo
调用了remove_if之后,v如下图所示:
todo
"要被删除"的指针已经被那些"不会被删除"额指针覆盖了,没有任何指针再指向Widget B和Widget C,所以它们永远不会被删除,资源泄漏。
remove_if和erase都返回之后,v如下图所示:
todo
得出结论:当容器中存放的是指向动态分配的对象的指针时,应该避免使用remove和类似的算法(remove_if和unique)。
解决该问题的方法:在进行erase-remove习惯用法之前,先把那些指向未被验证过的Widget的指针删除并置为空,然后清除该容器中所有的空指针。
// pWidget如果是未被验证的Widget,则删除该指针,并把它置为空
void delAndNullifyUncertified(Widget* pWidget) {
if (!pWidget->isCertified()) {
delete pWidget;
pWidget = 0;
}
}
// 将未被验证的Widget对象的指针删除并置为空
for_each(v.begin(), v.end(), delAndNullifyUncertified);
// 删除空指针
v.erase(remove(v.begin(), v.end(), static_cast<Widget*>(0)), v.end());
注意:容器中如果存放的是具有引用计数功能的智能指针,那么可以直接使用erase_remove_if习惯用法。
vector<Widget*> v;
...
v.push_back(make_shared(new Widget));
// 删除那些指向未被验证过的Widget对象的指针
// mem_fun见第41条
v.erase(remove_if(v.begin(), v.end(), not1(mem_fun(&Widget::isCertified))), v.end());