[STL] list的使用
1. list功能
list是双向循环链表,每一个元素都知道前面一个元素和后面一个元素.list对象自身提供了两个pointer用来指向第一个和最后一个元素.每个元素都有pointer指向前一个和后一个元素.如果想要插入新元素,只需要操纵对应的pointer即可.因此list在几个方面与array,vector不同:
- list不支持随机访问,如果你要访问第5个元素,就得顺着串链逐一爬过前4个元素.所以在list中随机访问一个元素是很缓慢的,然而你可以从两端开始航行整个list,所以访问第一个或最后一个元素速度很快.
- 任何位置上(不只两端)执行元素的插入或删除都非常快,因为无需移动任何其他元素.实际上内部只是进行了一些pointer操作而已.
- 插入和删除动作并不会造成其他元素的各个pointer,reference和iterator失效.
- list对于异常的处理方式是:要么操作成功,要么什么都不发生,绝对不会陷入”只成功一半”这种进退维谷的境地.
list所提供的成员函数反映出它与array,vector不同:
- list提供front(),push_front()和pop_front(),back(),push_back()和pop_back()等操作函数.
- 由于不支持随机访问,list既不提供下表操作符,也不提供at().
- list并未提供容量,空间重新分配等操作函数,因为完全无必要.每个元素都有自己的内存,在这个元素删除前一直有效.
2.list操作
在使用list前必须包括头文件#include
本文例子中到的两个list对象c1,c2分别为c1(10,20,30) c2(40,50,60)。还有一个迭代器it = list::iterator用来指向c1或c2元素。
1)、list的构造函数和析构函数
listc; // 构造函数,产生一个空list,没有任何元素 listc(c2); // copy 构造函数,建立c2的同类型list,并成为c2的一份拷贝 listc = c2; // copy 构造函数,建立一个新的list作为c2的拷贝 listc(rv); // move 构造函数,建立一个新的list,取rvalue rv的内容(始自C++11) listc = c2; // move 构造函数,建立一个新的list,取rvalue rv的内容(始自C++11) listc(initlist); // 建立一个list,以初值列initlist的元素为初值(始自C++11) listc = initlist; // 建立一个list,以初值列initlist的元素为初值(始自C++11) listc(n); // 利用元素的default构造函数生成一个大小为n的list listc(n,elem); // 建一个含n个元素的list,值都是elem listc(c1.begin(),c1.end()); // c含c1一个区域的元素[First, _Last)。 c.~list(); // 销毁所有元素,释放内存
2)、list的非易型操作
c.empty() // 返回容器是否为空(相当于size()==0但也许较快) c.size() // 返回目前的元素个数 c.max_size() // 返回元素个数之最大可能量 c1 == c2 // 返回c1是否等于c2(对每个元素调用==) c1 != c2 // 返回c1是否不等于c2(相当于!(c1 == c2)) c1 > c2 // 返回c1是否大于c2 c1 >= c2 // 返回c1是否大于等于c2 c1 < c2 // 返回c1是否小与c2 c1 <= c2 // 返回c1是否小与等于c2
3)、assign() 给list赋值:
c.assign(n,elem); // 复制n个elem,赋值给c c.assign(beg,end); // 将区间[beg,end)内的元素赋值给c c.assign(initlist); // 将初值列initlist所有元素赋值给c
4)、swap() 交换两个list
c1.swap(c2); // 置换c1和c2的数据 swap(c1,c2); // 置换c1和c2的数据
5)、list元素直接访问
front() 返回第一个元素 (不检查是否存在第一个元素) int i=c1.front(); // i=10 back() 返回最后一个元素 (不检查是否存在最后一个元素) int i=c1.back(); // i=30
6)、迭代器相关函数
begin() 返回一个迭代器,指向第一个元素 it=c1.begin(); // *it=10 end() 返回一个迭代器,指向最后一个元素的下一位置 it=c1.end(); //*(--it)=30; rbegin() 返回一个反向迭代器, 指向反向迭代器第一个元素 list::reverse_iterator riter=c1.rbegin(); // *riter=30 rend() 返回一个反向迭代器, 指向反向迭代器最后一个元素的下一个位置 list::reverse_iterator riter=c1.rend(); // *(--riter)=10 cbegin() 返回一个const迭代器, 指向第一个元素 (始自C++11) list::const_iterator citer=c1.cbegin(); // *citer=10且为const cend() 返回一个const迭代器, 指向最后一个元素的下一个位置 (始自C++11) list::const_iterator citer=c1.cend(); // *(--citer)=30且为const crbegin() 返回一个const反向迭代器, 指向反向迭代器第一个元素 (始自C++11) list::const_reverse_iterator criter=c1.crbegin(); // *criter=30且为const crend() 返回一个const反向迭代器, 指向反向迭代器最后一个元素的下一个位置 (始自C++11) list::const_reverse_iterator criter=c1.crend(); // *(--criter)=10且为const
7)、clear() 删除所有元素
c1.clear(); //c1为空 c1.size为0;
8)、erase() 删除一个元素 , 注意事项参照下面”注意事项1”
c1.erase(pos); // 移除pos位置上的元素,返回下一个元素的位置 c1.erase(beg,end); // 移除区间[beg,end)内所有元素,返回下一个元素的位置
9)、remove() 从list删除元素
c.remove(val); // 移除所有其值为val的元素
10)、remove_if() 按指定条件删除元素
c1.remove_if(op); // 移除所有"造成op(elem)结果为true"的元素
11)、resize() 改变list的大小
c1.resize(num) // 将元素数量改为num(如果size()变大,多出来的新元素都需以default构造函数完成初始化) c1.resize(num,elem) // 将元素数量改为num(如果size()变大,多出来的新元素都是elem的拷贝)
12)、insert() 插入一个元素到list中
c.insert(pos,elem); // 在iterator位置pos之前插入一个elem拷贝,并返回新元素的位置 c1.insert(pos,n,elem); // 在iterator位置pos之前插入n个elem拷贝,并返回第一个新元素的位置(或返回pos--如果没有新元素的话) c1.insert(pos,beg,end); // 在iterator位置pos之前插入区间[beg,end)内所有元素的一份拷贝,并返回第一个新元素的位置(或返回pos--如果没有新元素的话) c1.insert(pos,initlist);// 在iterator位置pos之前插入初值列initlist内所有元素的一份拷贝,并返回第一个新元素的位置(或返回pos--如果没有新元素的话;始自C++11) 例如: c1.insert(++c1.begin(),100); // c1(10,100,20,30) c1.insert(c1.begin(),2,200); // c1(200,200,20,30); c1.insert(++c1.begin(),c2.begin(),--c2.end()); // c1(10,40,50,20,30); c1.insert(++c1.begin(),{100,200}); // c1(10,100,200,20,30)
13)、emplace() 插入以args为初值的元素
c.emplace(pos,args...) // 在iterator位置pos之前插入一个以args为初值的元素,并返回新元素位置(始自C++11) c.emplace_back(args...) // 在末尾插入一个以args为初值的元素,不返回任何东西(始自C++11) c.emplace_front(args...) // 在起点插入一个以args为初值的元素,不返回任何东西(始自C++11)
14)、pop_back() 删除最后一个元素 ,但是不返回它
c1.pop_back(); // c1(10,20);
15)、pop_front() 删除第一个元素 ,但是不返回
c1.pop_front(); // c1(20,30)
16)、push_back() 在list的末尾添加一个元素
c1.push_back(100); // c1(10,20,30,100)
17)、push_front() 在list的头部添加一个元素
c1.push_front(100); // c1(100,10,20,30)
18)、merge() 合并两个list 并使之默认升序(也可改)
c.merge(c2) // 假设c和c2容器都包含op()准则下的已排序元素,将c2中的全部元素转移到c,并保证合并后的list仍已排序 c.merge(c2,op) // 假设c和c2容器都包含已排序元素,将c2中的全部元素转移到c,并保证合并后的list在op()准则下已排序 例如: c2.merge(c1); //c1现为空;c2现为c2(10,20,30,40,50,60) c2.merge(c1,greater()); //同上,但c2现为降序
19)、reverse() 把list的元素倒转
c1.reverse(); // c1(30,20,10)
20)、sort() 给list排序, 默认升序(可自定义)
c.sort() // 以operator<为准则对所有元素排序 c.sort(op) // 以op()为准则对所有元素排序 例如: c1.sort(); // c1(10,20,30) c2.sort(greater()); //同上,但c1现为降序
21)、splice() 合并两个list
c.splice(pos,c2) // 将c2内的所有元素转移到c之内,迭代器pos之前 c.splice(pos,c2,c2pos) // 将c2内的c2pos所指元素转移到c内pos之前 c.splice(pos,c2,c2beg,c2end) // 将c2内的[c2beg,c2end)区间内所有元素转移到c内pos之前 例如: c1.splice(++c1.begin(),c2); //c1(10,40,50,60,20,30) c2为空 全合并 c1.splice(++c1.begin(),c2,++c2.begin()); //c1(10,50,20,30) ; c2(40,60) 指定元素合并 c1.splice(++c1.begin(),c2,++c2.begin(),c2.end()); //c1(10,50,60,20,30); c2(40) 指定范围合并
22)、unique() 删除list中重复的元素
c.unique() // 如果存在若干相邻而数值相同的元素,就移除重复元素,只留一个 c.unique(op) // 如果存在若干相邻元素都使op()的结果为true,则移除重复元素,只留一个 例如: c1.unique(); // 假设c1开始为(-10,10,10,20,20,-10),则之后为c1(-10,10,20,-10)
以上就是list的基本用法,实际使用中还需要注意一下几点,后续遇到再加补充.
注意事项:erase使用注意 参考:http://blog.sina.com.cn/s/blog_66f74d9f0100om0f.html
常用的删除容器中元素的方法是如下(方法1):
list< int> List; list< int>::iterator iter; for( iter = List.begin(); iter != List.end(); ) { if(1) { iter = List.erase( iter ); } else { iter++; } } 也可以这样写(方法2): list< int> List; list< int>::iterator iter; for( iter = List.begin(); iter != List.end(); ) { if(1) { List.erase( iter++ ); } else { iter++; } } 有一种错误的写法(注意同方法2比较) list< int> List; list< int>::iterator iter; for( iter = List.begin(); iter != List.end(); ) { if(1) { List.erase( iter ); } iter++; } 我们看一下erase()函数的源代码(仅列出release下的代码)。 iterator erase(iterator _Where) { // erase element at _Where _Nodeptr _Pnode = (_Where++)._Mynode(); if (_Pnode != _Myhead) { // not list head, safe to erase _Nextnode(_Prevnode(_Pnode)) = _Nextnode(_Pnode); _Prevnode(_Nextnode(_Pnode)) = _Prevnode(_Pnode); this->_Alnod.destroy(_Pnode); this->_Alnod.deallocate(_Pnode, 1); --_Mysize; } return (_Where); } 函数在返回的时候,是返回当前迭代器的下一个节点。所以当 iter = List.erase( iter ); 执行以后,迭代器自动指向了下一个元素。而对于入参中的iter,所指的地址已经被销毁,所以写的时候,应该注意加上前面的iter = 那另外的一种写法,List.erase( iter++ ); 为什么也是对的呢? 研究了一下,这里需要讲一下++运算符的操作. _Myt_iter& operator++() { // preincrement ++((_Mybase_iter )this); return (*this); } _Myt_iter operator++(int) { // postincrement _Myt_iter _Tmp = *this; ++*this; return (_Tmp); }
++实际上可以看做是一个函数。
对于++在后的情况(例如i++),函数在运行的时候,将运算的数据i已经改变,但是函数的返回值是操作之前的数据,所以在我们看来,i++好像是先进行了i的读取,才+1。
回到迭代器,List.erase( iter++ );就没有问题了。
对于那种错误的方法,List.erase( iter );在执行以后,iter所指的对象已经被销毁,所以再对iter进行操作是非法的,程序会出错。
上一篇: Opencv4.0:遍历Mat图像空间、读取摄像头
下一篇: C中各关键字总结