[5 算法] 33. 对包含指针的容器使用remove这一类算法时要特别小心

假如现在有一些动态分配的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());

猜你喜欢

转载自blog.csdn.net/u012906122/article/details/119608020
今日推荐