Effective STL 學習筆記 32 ~ 33

1 記得 Remove 後要 Erase

Item 9 中已經提到過,若是真的想要用 remove 從容器中刪除元素,必需要在 remove 以後使用 erase 。其緣由在於: remove 接受的參數是通用的迭代器,而不是容器,它不知道容器的任何信息,也就沒法從容器中刪除元素: remove doesn't really remove anything, because it can't. css

remove 只保證在其返回的迭代器以前的元素爲有效數據(符合條件的數據),但它既沒法刪除被 remove 的數據,也不能保證返回的迭代器以後的數據位無效數據(不符合條件的數據)。這一點與某些容器內置的 remove member function 不同:這些 member function 會真正的把數據刪除掉: html

// 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 的話,會發生什麼事情? java

 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());

內存泄露。 ios

那麼將第 13 行換成下面的表達式呢? c++

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 或者內存泄露。 sql

咱們應該保證在 remove 過程當中,一旦發現不合要求的數據,立刻將其刪除,例以下面的例子: shell

#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;
}

其輸出爲: bash


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 $ 
相關文章
相關標籤/搜索