概述
一个合格的容器(Container)必须支持增删改查,C++的顺序容器和关联容器也不例外。
不同于insert和search,erase操作涉及到多种情况,尤其是和迭代器相结合的时候。
本文总结常用C++常用容器的erase的正确做法。
- 顺序容器:vector,dequeue,list,string
- 关联容器:set,map,unordered_set, unordered_map
顺序容器
vector
vector是STL中唯一一个保证内存连续的容器,也是我们最常用的容器。
删除值为val的元素
void EraseVec()
{
std::vector<int> vec{ 1, 2, 3, 4, 5, 5, 6, 7 };
vec.erase(std::remove(vec.begin(), vec.end(), 5), vec.end());
}
上述代码是最标准的在vector中删除值为5的元素的方法,你会发现有两个很容易让人产生误会的接口:
erase和remove,我们看一下他们的原型:
iterator erase (iterator begin, iterator end);
//tips : 删除容器中迭代器在[first; last)范围内的元素
//return : 最后一个被删除的元素的下一个元素的迭代器
iterator remove(iterator begin, iterator end, val);
//tips : 移除容器中迭代器在[first; last)范围内的值为val的元素
//return : 被成功移除的之后,剩余的有效元素的下一个元素的迭代器
我们可以看到,remove做的操作是移除而erase是删除,这两者到底有什么区别呢?
我们刚创建的vec的内存布局如下:
在remove(begin,end,5)之后,内存布局如下:
它仅仅做了移除操作,把不要的元素放在了末尾(这是其中一种实现,你不能对67后面的两个位置的值做出任何假设),但是capacity甚至size都没有改变,也就是说元素并没有被删除。
更可怕的是,如果你做了下列操作:
std::remove(vec.begin(), vec.end(), 5);
for (auto it = vec.begin(); it != vec.end(); ++it)
{
printf("%d\n", *it);
}
你会发现输出的值是12346755(或者可能是132456767),元素根本没有被删除
在vec.erase(std::find(vec.begin(), vec.end(), 5))