Effective STL 学习笔记 32 ~ 33
Table of Contents
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 $
(转载请注明出处,使用许可:署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议 。)