STL基础6:list容器的使用总结
一.list使用构造函数的四种初始化方式list的初始化方式和vector基本一样://1.默认构造函数,长度为0的列表 list lis1; //2.带有单个整形参数的构造函数,长度为50的列表 list lis2(50); list lis3(50,1);//长度为50,初始值为1的列表 //3.复制构造函数,构造一个新的列表lis4,作为已存在的列表lis3的完全
一.list使用构造函数的四种初始化方式
list的初始化方式和vector基本一样:
//1.默认构造函数,长度为0的列表
list<int> lis1;
//2.带有单个整形参数的构造函数,长度为50的列表
list<int> lis2(50);
list<int> lis3(50,1);//长度为50,初始值为1的列表
//3.复制构造函数,构造一个新的列表lis4,作为已存在的列表lis3的完全复制
list<int> lis4(lis3);
//4.带两个常量参数的构造函数,产生初始值为一个区间的向量。区间由一个半开区间[first,last) 来指定
list<int> lis5(lis3.begin(),lis3.end());
SAMPLES:
int v1[10] = {0,1,0,0,3,0,0,4,4,4};
//第一种初始化方式:带有单个整形参数的构造函数
list<int> lis1(10);
for(int i=0;i<10;i++)
{
//将数组v1的元素添加到列表init1的最后
lis1.push_back(v1[i]);
}
注意:这里的push_back和向量的push_back不一样的地方在于,列表增加元素是不会全部重新分配内存的,所以列表list增加元素效率比向量vector的高很多
//第二种初始化方式:带两个迭代器参数的构造函数
//左闭右开区间,左边从数组[0]的地址开始拷贝数组元素,到右边的数组[10]之前一个的地址结束
//也就是说如果要拷贝数组v1里的10个元素,就必须在右边,数组最后一个元素的地址再加上1(&v1[9]+1)
list<int> lis2(&v1[0],&v1[9]+1);
//第三种初始化方式:默认构造函数
list<int> lis3;
lis3.insert(lis3.begin(),&v1[0],&v1[9]+1);//后面两个参数同样也是左闭右开
//第四种初始化方式:复制构造函数
list<int> lis4(lis3);
二.list的遍历向量里元素的两种方式
//第一种遍历方式:使用迭代器
int v2[10] = {0,1,2,3,4,5,6,7,8,9};
list<int> lisForeach1(&v2[0],&v2[9]+1);
typedef list<int>::iterator List_Iter;
List_Iter iterEnd=lisForeach1.end();
for(List_Iter iter=lisForeach1.begin();iter!=iterEnd;++iter)
{
printf("使用迭代器遍历列表方式取lisForeach1里的元素为%d\n", *iter);
}
//第二种遍历方式:使用STL的算法类里的for_each,需要包含头文件#include <algorithm>,还需要
//定义一个List_ForeachCoutFun的方法将元素输出
首先需要定义一个List_ForeachCoutFun的方法将元素输出
void List_ForeachCoutFun(int &outIndex)
{
printf("使用foreach输出lisForeach1里的元素为%d\n",outIndex);
}
然后调用for_each将元素输出
for_each(lisForeach1.begin(),lisForeach1.end(),List_ForeachCoutFun);
三.list的删除操作
第一种删除方式:使用list的成员函数erase来删除
int v3[10] = {0,1,2,3,4,5,6,7,8,9};
list<int> lisDeleted1(&v3[0],&v3[9]+1);
typedef list<int>::iterator List_Iter;
for(List_Iter iter=lisDeleted1.begin();iter!=lisDeleted1.end();iter++)
{
//list容器的erase之后,删除元素的迭代器就会失效,所以,下次在使用该失效的迭代器++,是不会得到
//原先迭代器序列中的下个迭代器的,所以程序会报错
lisDeleted1.erase(iter);
}
这是一段使用迭代器删除lisDeleted1中所有元素的代码,正像上面的注释一样,程序报错了,erase第一次执行完后,删除了lisDeleted1的第一个元素0,删除完后将失效的迭代器iter++,当然会报错,那么就应该像向量一样,更新一下erase返回的迭代器吧,改成iter=lisDeleted1.erase(iter);,,没错这样就不报错了,元素没有全部删掉,达不到很好的效果,于是本人不小心在网上找了一段让我纠结了很久的代码,这段代码可以用最少代码量同样使用遍历完成删除全部元素,代码如下:
for(List_Iter iter=lisDeleted1.begin();iter!=lisDeleted1.end();)
{
lisDeleted1.erase(iter++);
}
没看错,就是用上面这段代码完成了删除所有元素的“梦想”,这段代码和上面的代码有个不同的地方就是将iter++作为erase的参数执行,有些人可能就会奇怪了,这两端代码不是看起来“一模一样”吗————不都是先执行删除迭代器,然后再进行自增吗,这样也就会像上面的代码一样,失效的迭代器自增,然后就报错了,然后就没有然后了。。。。。。。。但是我想说,执行起来差很大啊。(注:已经很清除前++和后++最本质的区别的童鞋,就别看下面的分析了)
一段 STL list容器使用erase删除元素的 代码引来的思考:
没错,应该做些文章讲解上面的代码,因为这牵扯到一个最基本的运算知识,前++和后++的区别,也许就像以前我上大学那本教科书上说的
int a=5;
int b=a++;
后++,先运算,后自增(前++,先自增,后运算),所以执行完这句话后b=5,a=6,我想说,写了这么就代码,这段口诀真是好啊,让我都不用去想,以为是先运算b=a,再将a自加1.然后得出一个正确的结果,然后将这句口诀到哪都屡试不爽,如果你还相信这句“坑爹的”口诀的,那你可以将这句口诀套在上面这段代码上lisDeleted1.erase(iter++);,套完后,这句代码的解释就是先执行erase,将迭代器指向的元素删除,然后迭代器在自增,???????等等,,哪里不对啊???迭代器iter不是在执行完erase就失效了吗,再自增就肯定报错啦,太诡异了,屡试不爽的口诀(后++,先运算,后自增),怎么到这不管用了。。。。。
呵呵,我只想说,谢谢STL的代码编写人员,谢谢你们提供了源码,开源的好处就是让你可以了解内部的实现,没错,口诀都是浮云,要了解内部实现的机制才是王道啊,好了,再次贴出迭代器的前++和后++代码(前面的章节讲过两者的区别,不是很深入)
后置++代码:
_Myiter operator++(int)
{ // postincrement
_Myiter _Tmp = *this;
++*this;
return (_Tmp);
}
嗯,现在看起来这段后置++的代码好亲切啊,最关键的一句是return (_Tmp);没错,看看_Tmp是什么,_Myiter _Tmp = *this,this就是指向迭代器iter的指针,迭代器本身就是指针,那就是说this是指向指针的指针,*this就是使用*操作符取指向迭代器的指针的值,也就是说*this=迭代器,所以_Tmp=迭代器,那么抛弃中间的++*this代码,也就是说迭代器后置++执行完后,返回的还是迭代器本身的值(确切的说是一个迭代器的副本,也就是另外开辟了一段内存存这个迭代器的值,这种方式确实效率低啊,不过这是有用的),也就是说外部在执行后置++完后得到的值是一个原本迭代器的值的副本(_Tmp),但是迭代器本身*this确做了一次前置++的运算,那么迭代器的值经过这次后置++,本身迭代器iter确实改变了值。
所以说了这么多,得出一个最简单的结论,就是后置++运算(如iter++)这句话执行完后,iter++=iter,但是iter=++iter;也就是说(iter++)这个“整体”得到的值还是原来的iter的一个副本,如果谁使用了这个后置++的“整体”,那么其实还是调用iter这个原本的值,然后iter++这句话执行完后,iter自身前置++一次了。
嗯,现在将lisDeleted1.erase(iter++);这句话这样解释:iter++这句话执行完后,产生了一个迭代器iter的副本(同样的数据,内存不同),然后迭代器iter自身前置++了一遍,所以iter本身改变了,但是他的“副本”还是原先的值,所以erase里的参数实际执行的还是那个“副本”(原先的那个迭代器iter值),执行完后“副本”迭代器失效了,但是“真身”迭代器iter早就自++了,所以下次在调用iter,这个值就是erase删除元素的下个迭代器了,就这样用个for循环,将所有list元素都巧妙的删除。
说完后发现原来后置++在这个迭代器的删除上有这么大的用途啊。
让我们顺便看看前置++的代码:
_Myiter& operator++()
{
++(*(_Mybase *)this);
return (*this);
}
首先将this(指向迭代器的指针)转换成_Mybase类型的指针---(_Mybase *)this,然后取这个指针的值,也就是取出迭代器-----(*(_Mybase *)this),然后将这个迭代器++,然后,然后最重要的是前置++返回的就是这个改变了的迭代器,没有什么“副本”,虽然效率确实高了,但是后置++的用途还是有的啊。
于是摒弃坑爹的教科书口诀,再看看经典的后置++例子:
int a=5;
int b=a++;
按照上面说的,这个int型的后置++虽然没有源码可跟,但是我很能肯定的说,他跟迭代器的后置++一样的实现,也是执行完a++后,这个a++“整体”产生了一个“副本”,这个副本还是原先a的值5,所以b= “副本”=5;而a再执行完a++后本身变成了6。
说到这里,再次感谢STL源码的,支持开源。
第二种删除方式:使用list的成员函数remove()来删除
第三种删除方式:使用list的成员函数remove_if()来删除(从list中按指定条件删除元素)
第四种删除方式:使用list的成员函数clear()来删除(删除所有元素)
第五种删除方式:使用list的成员函数pop_back()来删除(删除最后一个元素)
第六种删除方式:使用list的成员函数pop_front()来删除(删除第一个元素)
第七种删除方式:使用list的成员函数unique()来删除(删除list中的重复元素)
三.list的插入操作
第一种插入方式:使用list的成员函数insert()来插入
第二种插入方式:使用list的成员函数push_back()来插入(在list的末尾添加一个元素)
第三种插入方式:使用list的成员函数push_front()来插入(在list的头部添加一个元素)
更多推荐
所有评论(0)