STL基础6:list容器的使用总结

一.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
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值