Effective STL 学习笔记 32 ~ 33

Effective STL 学习笔记 32 ~ 33

1 记得 Remove 后要 Erase

Item 9 中已经提到过,如果真的想要用 remove 从容器中删除元素,必须要在 remove 之后使用 erase 。其原因在于: remove 接受的参数是通用的迭代器,而不是容器,它不知道容器的任何信息,也就无法从容器中删除元素: remove doesn't really remove anything, because it can't.

remove 只保证在其返回的迭代器之前的元素为有效数据(符合条件的数据),但它既无法删除被 remove 的数据,也不能保证返回的迭代器之后的数据位无效数据(不符合条件的数据)。这一点与某些容器内置的 remove member function 不一样:这些 member function 会真正的把数据删除掉:

// For vector, we have to call erase after remove.
vector<int> v;
// ....
vector<int>::iterator endPos = remove(v.begin(), v.end(), 99); // Remove 99 from vector, returns last valid position.
v.erase(posEnd, v.end()); // Erase all elements after posEnd.

// For list, the member function remove is enough:
list<int> l;
//...
l.remove(99); // this will erase all 99 in the list.

2 remove, container, pointer

Item 6 中提到,在容器中放置对象指针容易混乱,这里如果对存放指针的容器调用 remove-erase idom 的话,会发生什么事情?

 1: class Widget
 2: {
 3: public:
 4:     Widget();
 5:     virtual ~Widget();
 6:     bool IsCertified();
 7: };
 8: 
 9: vector<Widget*> v;
10: 
11: // push lots of widget pointers to v.
12: 
13: v.erase(remove(v.begin(), v.end(), not1(mem_fun(&Widget::IsCertified))), v.end());

内存泄露。

那么将第 13 行换成下面的表达式呢?

14: vector<Widget*> endPos = remove(v.begin(), v.end(), not(mem_fun(&Widget::IsCertified)));
15: for_each(endPos, v.end(), [](Widget* pw){if(pw) delete pw;});
16: v.erase(endPos, v.end());

第 14 行将所有的 endPos 之后的指针先释放,然后再去 erase。前面刚刚提过, remove 不会保证返回的迭代器之后的元素都为无效值,第 14 行有可能会将本该保留的对象给释放掉,还有可能会将该释放的不释放。结果可能会 Crash 或者内存泄露。

我们应该保证在 remove 过程中,一旦发现不合要求的数据,马上将其删除,例如下面的例子:

#include <vector>
#include <iterator>
#include <algorithm>
#include <iostream>
#include <stdio.h>

using namespace std;

class Widget
{
public:
    Widget(int i);
    virtual ~Widget();
    bool IsCertified();
    void Show();
private:
    int m_i;
};

/* See description in header file. */
Widget::Widget(int i)
        : m_i(i)
{
}

/* See description in header file. */
Widget::~Widget()
{
    printf ("Deleting Obj: %p, value: %d\n", this, m_i);
}

/* See description in header file. */
bool Widget::IsCertified()
{
    return m_i % 2 == 0;
}

/* See description in header file. */
void Widget::Show()
{
    printf ("\tObj: %p, value: %d\n", this, m_i);
}

bool DeleteIfUncitified(Widget* p)
{
    if (p && !p->IsCertified())
    {
        delete p;
        return true;
    }
    return false;
}

int main(int argc, char *argv[])
{
    vector<Widget*> v;
    for (int i = 0; i < 20; ++i)
    {
        Widget* p = new Widget(i);
        v.push_back(p);
    }

    random_shuffle(v.begin(), v.end());

    printf ("Before remove, size: %lu, contents:\n", v.size());
    for_each(v.begin(), v.end(),
             [](Widget* p){
                 if (p) p->Show();
                 else printf("Obj is Null");});
    printf ("\nNow removing...\n");
    v.erase(remove_if(v.begin(), v.end(), DeleteIfUncitified), v.end());
    printf ("\nAfter remove, size: %lu, contents:\n", v.size());
    for_each(v.begin(), v.end(),
             [](Widget* p){
                 if (p) p->Show();
                 else printf("Obj is Null");});

    return 0;
}

其输出为:


Welcome to the Emacs shell

~/tmp $ ./test
Before remove, size: 20, contents:
        Obj: 0x7f8b31c038d0, value: 19
        Obj: 0x7f8b31c03870, value: 3
        Obj: 0x7f8b31c039e0, value: 14
        Obj: 0x7f8b31c039f0, value: 15
        Obj: 0x7f8b31c03890, value: 10
        Obj: 0x7f8b31c03850, value: 2
        Obj: 0x7f8b31c03900, value: 6
        Obj: 0x7f8b31c038b0, value: 17
        Obj: 0x7f8b31c03920, value: 8
        Obj: 0x7f8b31c038a0, value: 4
        Obj: 0x7f8b31c039c0, value: 12
        Obj: 0x7f8b31c03910, value: 7
        Obj: 0x7f8b31c038f0, value: 5
        Obj: 0x7f8b31c039b0, value: 11
        Obj: 0x7f8b31c03860, value: 1
        Obj: 0x7f8b31c03880, value: 9
        Obj: 0x7f8b31c03840, value: 0
        Obj: 0x7f8b31c03a00, value: 16
        Obj: 0x7f8b31c039d0, value: 13
        Obj: 0x7f8b31c038c0, value: 18

Now removing...
Deleting Obj: 0x7f8b31c038d0, value: 19
Deleting Obj: 0x7f8b31c03870, value: 3
Deleting Obj: 0x7f8b31c039f0, value: 15
Deleting Obj: 0x7f8b31c038b0, value: 17
Deleting Obj: 0x7f8b31c03910, value: 7
Deleting Obj: 0x7f8b31c038f0, value: 5
Deleting Obj: 0x7f8b31c039b0, value: 11
Deleting Obj: 0x7f8b31c03860, value: 1
Deleting Obj: 0x7f8b31c03880, value: 9
Deleting Obj: 0x7f8b31c039d0, value: 13

After remove, size: 10, contents:
        Obj: 0x7f8b31c039e0, value: 14
        Obj: 0x7f8b31c03890, value: 10
        Obj: 0x7f8b31c03850, value: 2
        Obj: 0x7f8b31c03900, value: 6
        Obj: 0x7f8b31c03920, value: 8
        Obj: 0x7f8b31c038a0, value: 4
        Obj: 0x7f8b31c039c0, value: 12
        Obj: 0x7f8b31c03840, value: 0
        Obj: 0x7f8b31c03a00, value: 16
        Obj: 0x7f8b31c038c0, value: 18
~/tmp $ 
posted @ 2013-11-15 21:24  英超  Views(200)  Comments(0Edit  收藏  举报