Table of Contents
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 $