stl之顺序容器学习笔记1

初窥容器

容纳特定类型对象的集合,称为容器。

将单一类型的元素聚集起来成为容器,然后根据位置来存储和访问元素,称为顺序容器。顺序容器的排列次序,按照元素添加到容器的次序决定。

stl定义了三种顺序容器:

1、vector

2、list

3、deque

他们的差异在于访问元素的方式,以及做添加、删除元素时的运行代价不同。

1、vector拥有快速的随机访问能力(利用下标);快速的在容器尾部添加和删除元素,但在其他位置插入和删除元素,则会付出较多的代价。

2、list可在容器的任意位置插入或删除元素,且高效。在元素访问方面,只支持顺序访问而不支持随机访问(必须通过自增或自减迭代器)。

3、deque为双端队列,类似于vector,唯一不同的是可以快速在容器头部或尾部插入或删除元素。

与容器相关的:

iterator:迭代器,是一种类型,其作用是遍历容器中的元素。所有的标准库容器都定义了迭代器。

stl的迭代器支持解引用*(取指向的元素的值),自增++(指向下一元素),自减--(指向前一元素),相等==(如两迭代器指向同一元素),不等!=运算符操作符。

举个具体的示例,此示例实现了向容器中添加元素,并顺序输出元素。

 vector<int> ivec;//定义一个vector容器,元素为int型
 vector<int>::iterator iter;//定义一个vector<int>类型的迭代器
 int num;
 while(num!=-1)
 {//向vector中添加元素,-1作为结束标志
  cin >>num;
  ivec.push_back(num);
 }

 iter=ivec.begin();//将iter指向第一个元素
 for(;iter!=ivec.end();++iter)
 {//iter不等于超出容器末端的第一个元素时,即循环输出
  cout <<*iter <<endl;
 }

当然以上程序若要正确编译,还要包含相应的头文件

#include <vector>

#include <list>

#include <deque>

所有的容器都是类模板,在定义时,需提供相应的元素类型,如vector<int>,可以把vector想象成一个罐子,至于罐子中是放糖还是毒药,由我们自己定义。

当然也可以定义。list<string>,deque<double>..所有的容器都带有默认的构造函数,用于创建一个空的容器对象。

容器的构造函数

除了默认构造函数外,stl容器提供了如下几个构造函数,非常符合人们的思维方式:

1、C<T> c;默认构造函数,用于创建一个空的容器。

如:

vector<int> ivec;

2、C<T> c1(c);使用c的副本初始化c1。c和c1必须具有相同的容器类型。适用于所有类型的容器。如:

vector<int> ivec1;

....//向ivec1中加入元素

vector<int> ivec2<ivec1>;

3、C<T> c1(b,e);使用b,e迭代器之间的元素副本初始化c1。b标识了首复制元素,e标识了尾复制元素。适用于所有类型的容器。看以下示例

vector<int> ivec;

vector<int>::iterator iter_mid,iter;

....//向vector中增加元素

iter_mid=ivec.begin()+ivec.size()/2;

iter=ivec.begin();

list<int> ilist(iter,iter_mid);//利用vector的元素初始化list,完全可以,只要元素类型一致即可

4、C<T> c(n,t);使用n个值为t的元素对c进行初始化,t的类型必须为T。只适用于顺序容器。

如:

int a=5;

vector<int> ivec(10,a);

5、C<T> c(n);创建可以容纳n个T类型元素的容器,但不初始化元素。换句话说就是初始化容器的大小,但不初始化元素值。只适用于顺序容器。

vector<int> ivec(100);

容器元素的类型限制

容器元素的类型受以下两个基本条件限制:

1、元素类型必须支持赋值操作。

2、元素类型的对象,必须可以复制。

考虑如下的例子:

如类foo没有默认的构造函数,但有一个需提供int类型形参的构造函数(foo(int a);)那么

vector<foo> fvec;//ok,构造了一个空容器,不牵涉到元素初始化

vector<foo> fvec(10);//error,构造可容纳10个foo对象元素的容器,牵涉到元素构造函数,但未提供构造函数需要的参数

vector<foo> fvec(10,1);//ok,构造一个拥有10个1的容器,牵涉到元素的构造函数,也提供了构造函数需要的参数,可以成功初始化。

当然,我们可以定义容器的容器。

也就是说,可以定义一个vector,其中的元素类型为vector<int>。

vector< vector<int> > ivvect;

注意以上定义,元素类型前后,必须加空格。否则会出现编译错误,因为在std命名空间中有>>操作符,会发生错误的引用。

迭代器详述

前面初步描述了一下iterator迭代器类型,下面详细论述。

迭代器的常用操作:

1、*iter:解引用,返回iter指向的元素的值。

2、iter->mem:先对iter进行解引用,然后获取指定元素的mem成员。相当于(*iter).mem。

3、++iter/iter++:使iter指向下一个元素。

4、--iter/iter--:使iter指向前一个元素。

5、iter1==iter2/iter1!=iter2:两迭代器指向同一元素时,则相等。需注意,两迭代器指向超出末端的下一个元素时,也相等。

vector和deque容器迭代器的额外操作

只有vector和deque提供容器迭代器的算术运算集合和关系运算集合(除==和!=)

1、iter+n/iter-n:将迭代器指向前面或后面的n个元素。新计算出的迭代器必须指向容器中的元素或超出容器末端的下一位置。

2、iter1+=iter2/iter-=iter2:迭代器加减法的符合运算;将iter1加上或减去lter2的运算结果,赋给iter1。

3、iter1-iter2:两个迭代器的减法,其运算结果加上iter2即为iter1。这两个迭代器必须指向同一容器中的元素或超出容器末端的下一位置。

4、>,>=,<,<=:关系操作符,比较的是迭代器在容器中的位置。如iter1指向的元素位于iter2的前面,则iter1<iter2,反之亦然。参与关系运算的两个迭代器必须指向同一容器中的容器或容器超出末端的下一位置。

关系操作符只适用于vector和deque,因为只有这两种容器提供元素快速随即访问(通过元素位置,即下标)。

看一个例子:

//取vector容器的中点

vector<int>::iterator iter=ivec.begin()+ivec.size()/2-1;

list<int> ilist(ivec.begin(),ivec.end());

ilist.begin()+ilist.size()/2;//error,list没有定义+操作

迭代器范围

c++语言使用一对迭代器来标记迭代器范围,这两个迭代器指向了容器的元素或超出容器末端的第一个元素(即最后一个元素的下一个位置),常用begin和end表示。

这种元素范围(首元素/末端的下一位置)称为左闭合区间,其形式为:

[begin,end)

标示从begin开始,end结束,但不包括end。

形成此种元素范围的条件是:

1、两个迭代器均指向容器中的元素或超出末端的下一位置。

2、如果这两个迭代器不相等,则begin不断自增的情况下,必定能begin=end。也就是说,end不可能出现在begin之前。

使用左闭合区间的意义:
1、当begin和end相等时,迭代器范围为空。

2、当begin和end不相等时,范围内至少有一个元素,且begin指向该区间的第一个元素。根据上面第二条,begin不断自增,可以达到begin=end。

考虑以下的例子:
while(begin!=end)

{

  //如果begin==end,(根据意义1)则说明迭代器范围内没有元素,则进入不了循环。(根据意义2)如begin!=end,则范围内肯定有元素,可以安全的解引用。

  ++begin;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值