C++ 中删除顺序容器的操作有以下几种:
c.erase(p) 删除迭代器p所指向的元素,返回一个迭代器,它指向被删除元素后面的元素
c.erase(b,e) 删除迭代器b和e所标记的范围内的所有元素,返回一个迭代器,指向被删除元素段后面的元素。
c.clear() 删除容器c内的所有元素,返回void
c.pop_back() 删除容器c的最后一个元素,返回void
c.pop_front() 删除容器c的第一个元素,返回void,该操作只适用于list和deque容器。
需要注意的是,erase操作不会检查它的参数,所以必须确保用作参数的迭代器是有效的。
下面看题:
假设有如下ia的定义,将ia复制到一个vector容器和一个list容器中。使用单个迭代器参数版本的erase函数将list容器中的奇数值元素删除掉,然后将vector容器中的偶数值元素删除掉。
int ia[] = { 0,1,1,2,3,5,8,13,21,55,89 };
我觉得很容易,代码如下:
#include <iostream>#include <vector>#include <list>using namespace std;int main(){int ia[] = { 0,1,1,2,3,5,8,13,21,55,89 };vector<int> ivec(11);list<int> ilist(11);//复制过程vector<int>::iterator av = ivec.begin();list<int>::iterator al = ilist.begin();for (int i = 0;i < 11;i++) {*av = ia[i];*al = ia[i];av++;al++;}//删除部分vector<int>::iterator av1 = ivec.begin(); for (int i = 0;i < 11;i++) {if (i % 2 != 0)ivec.erase(av1);av1++;}list<int>::iterator al1 = ilist.begin();for (int i = 0;i < 11;i++) {if (i % 2 == 0)ilist.erase(al1);al1++;}//显示部分 cout << "vector: " << endl;for (vector<int>::iterator a = ivec.begin();a != ivec.end();a++)cout << *a << " ";cout << endl;cout << "list: " << endl;for (list<int>::iterator a = ilist.begin();a != ilist.end();a++)cout << *a << " ";cout << endl;return 0;}运行,结果不对。在linux下出现的问题是:Segmentation fualt (core dumped)
后来我想,肯定是在删除元素的时候,在迭代器的有效范围之外。
我们注意到删除部分:
//删除部分vector<int>::iterator av1 = ivec.begin(); for (int i = 0;i < 11;i++) {if (i % 2 != 0)ivec.erase(av1);av1++;}当删除一个元素时,也就是执行一次ivec.erase(av1);此时迭代器会指向哪儿呢?该操作的返回值是指向被删除元素后一个元素的迭代器,那么我想,删除一个元素之后,当前迭代器指向的也是下一个元素,这时再执行av1++;就使得在一个循环之内迭代器往后移了两位,所以最后会超出迭代器有效范围。
修改代码如下:
#include <iostream>#include <vector>#include <list>using namespace std;int main(){int ia[] = { 0,1,1,2,3,5,8,13,21,55,89 };vector<int> ivec(11);list<int> ilist(11);//复制过程vector<int>::iterator av = ivec.begin();list<int>::iterator al = ilist.begin();for (int i = 0;i < 11;i++) {*av = ia[i];*al = ia[i];av++;al++;}//删除部分vector<int>::iterator av1 = ivec.begin(); for (int i = 0;i < 11;i++) {if (i % 2 != 0)ivec.erase(av1);elseav1++;}list<int>::iterator al1 = ilist.begin();for (int i = 0;i < 11;i++) {if (i % 2 != 0)ilist.erase(al1);elseal1++;}//显示部分 cout << "vector: " << endl;for (vector<int>::iterator a = ivec.begin();a != ivec.end();a++)cout << *a << " ";cout << endl;cout << "list: " << endl;for (list<int>::iterator a = ilist.begin();a != ilist.end();a++)cout << *a << " ";cout << endl;return 0;}如我所料,vector部分显示正常,但是list又有问题。在网上找了一下,因为vector容器的元素是连续排列的,就像数组那样,所以可以用下标访问,而且迭代器可以使用类似p+n这样的运算,p是迭代器,n是常数,因为连续排列意味着知道其中一个元素的地址,就知道了所有元素的地址,每两个相邻的地址之间总是相差一个元素的长度大小。而list却不一样,它类似于链表,两个相邻元素之间地址相差多少根本不知道,因为它不是连续排列的,也因此我们没办法使用p+n这类的运算。在使用erase操作的时候,list每删除一个元素,迭代器就失效了,所以在删除时必须记录下一个元素的地址,这样才能保证程序的正常运行,而vector则不需要。
修改之后,代码如下:
#include <iostream>#include <vector>#include <list>using namespace std;int main(){int ia[] = { 0,1,1,2,3,5,8,13,21,55,89 };vector<int> ivec(11);list<int> ilist(11);//复制过程vector<int>::iterator av = ivec.begin();list<int>::iterator al = ilist.begin();for (int i = 0;i < 11;i++) {*av = ia[i];*al = ia[i];av++;al++;}//删除部分vector<int>::iterator av1 = ivec.begin(); for (int i = 0;i < 11;i++) {if (i % 2 != 0)ivec.erase(av1);elseav1++;}list<int>::iterator al1 = ilist.begin();for (int i = 0;i < 11;i++) {if (i % 2 == 0)al1 = ilist.erase(al1);elseal1++;}//显示部分 cout << "vector: " << endl;for (vector<int>::iterator a = ivec.begin();a != ivec.end();a++)cout << *a << " ";cout << endl;cout << "list: " << endl;for (list<int>::iterator a = ilist.begin();a != ilist.end();a++)cout << *a << " ";cout << endl;return 0;}总结:
1.要理解不同容器的内部构造到底是什么样子的,,而不是单纯的记忆个容器有哪些操作。
2.时刻记得迭代器是可能失效的,并且可能超出有效范围。
3.光想是没用的,一定要落实到每一行代码,哪怕觉得很简单。
思念是一种幸福的忧伤,是一种甜蜜的惆怅,是一种温馨的痛苦;