删除容器的元素时应谨慎
On 2011-02-25 22:35:00 By Soli删除容器的元素时应谨慎
当一块内存被释放后,指向它的所有指针都成了“野指针”;当容器中的的一个元素被删后,指向那个元素的所有迭代器也都失效了。这两者都是程序员的大敌。对于前者,你已经有了足够的警惕,并且多年来养成了一个对付它的好习惯,就是在释放内存后立即把指向它的指针赋值为 NULL ,并在使用任何指针前都先判空(判断其是否为空指针)。而对于后者,即使经验丰富的猎人也仍然不时地掉入它的陷阱。
假定我们有一个容器 c ,定义如下:
AssocContainer<int> c;
现在我们要删除 c 中包含特定值的元素,比如 2012。我们最先想到的可能是循环、判断然后删除,如下:
for (AssocContainer<int>::iterator i = c.begin();
    i!= c.end();
    ++i) {
    if (2012 == (*i)) c.erase(i);
}
看起来很简单,但不幸的是,这种做法含有未定义行为,是错误的。
正如前面所述,当容器中的的一个元素被删后,指向那个元素的所有迭代器也都失效了。当 c.erase(i) 返回时, i 已经失效。于是, for 循环中的 ++i 部分将成为未定义行为。
《Effective STL》中告诉了我们一种后置递增的方法,来保证在调用 erase 之前就得到了 c 中下一个元素的迭代器。如下:
for (AssocContainer<int>::iterator i = c.begin();
    i != c.end();
    /*nothing*/ ){  // for循环的第三部分是空的;i现在在下面自增
    if (2012 == (*i)) c.erase(i++); // 对于要删除的值,
                    // 把当前的i传给erase,然后递增i
    else ++i;   // 对于其他值,直接递增i
}
《Effective STL》还为我们对所有容器进行了总结。如下:
- 去除一个容器中有特定值的所有对象: - 如果容器是 vector、string 或 deque,使用 erase-remove 惯用法。 - c.erase(remove(c.begin(), c.end(), 2012), c.end());
- 如果容器是 list,使用 - list::remove。- c.remove(2012);
- 如果容器是标准关联容器,使用它的 - erase成员函数。- c.erase(2012);
 
- 去除一个容器中满足一个特定判定式的所有对象: - 比如,这个特定判定式定义如下: - bool badValue(int x) { return(x == 2012); }- 如果容器是 vector、string 或 deque,使用 erase-remove_if 惯用法。 - c.erase(remove_if(c.begin(), c.end(), badValue), c.end());
- 如果容器是 list,使用 - list::remove_if。- c.remove_if(badValue);- 如你所见,对于序列容器(vector、string、deque 和 list),我们要做的只是把每个 - remove替换为- remove_if即可。
- 如果容器是标准关联容器,写一个循环来遍历容器元素,当你把迭代器传给 erase 时记得后置递增它。 - //正如我们上面讲过的。
 
- 在循环内做某些事情(除了删除对象之外): - 如果容器是标准序列容器,写一个循环来遍历容器元素,每当调用 erase 时记得都用它的返回值更新你的迭代器。 - for (SeqContainer<int>::iterator i = c.begin(); i != c.end();){ if (2012 == (*i)){ // go on the Noah's Ark i = c.erase(i); // 通过把erase的返回值 } // 赋给i来保持i有效 else ++i; }
- 如果容器是标准关联容器,写一个循环来遍历容器元素,当你把迭代器传给 erase 时记得后置递增它。 - for (AssocContainer<int>::iterator i = c.begin(); i !=c.end();){ if (2012 == (*i)){ // go on the Noah's Ark c.erase(i++); // 删除元素,记得后置递增i } else ++i; }
 
 Except where otherwise noted, content on this site is licensed under a
		
			Creative Commons Attribution 4.0 International license
		
		.
		
		Except where otherwise noted, content on this site is licensed under a
		
			Creative Commons Attribution 4.0 International license
		
		.