C++STL容器和迭代器

1、简介

  容器(container)用于存放数据的类模板。可变长数组、链表、平衡二叉树等数据结构在 STL 中都被实现为容器。程序员使用容器时,就是将容器类模板实例化为容器类,需要指明容器中存放的元素是什么类型的
  容器中可以存放基本类型的变量,也可以存放对象。对象或基本类型的变量被插入容器中时,实际插入的是对象或变量的一个复制品。

1.1 容器分类

  容器分为2类:顺序容器和关联容器
  顺序容器:可变长动态数组 vector、双端队列 deque、双向链表 list。之所以被称为顺序容器,是因为元素在容器中的位置同元素的值无关,即容器不是排序的。将元素插入容器时,指定在什么位置(尾部、头部或中间某处)插入,元素就会位于什么位置。
  关联容器:set、multiset、map、multimap。
  关联容器内的元素是排序的。插入元素时,容器会按一定的排序规则将元素放到适当的位置上,因此插入元素时不能指定位置。默认情况下,关联容器中的元素是从小到大排序(或按关键字从小到大排序)的,而且用<运算符比较元素或关键字大小。因为是排好序的,所以关联容器在查找时具有非常好的性能。
  STL 还在两类容器的基础上屏蔽一部分功能,突出或增加另一部分功能,实现了三种容器适配器:栈 stack、队列 queue、优先级队列 priority_queue。
  容器都是类模板。它们实例化后就成为容器类。用容器类定义的对象称为容器对象。

2、容器的比较

  任何两个容器对象,只要它们的类型相同,就可以用 <、<=、>、>=、==、!= 进行词典式的比较运算。假设 a、b 是两个类型相同的容器对象,这些运算符的运算规则如下:
  a == b:若 a 和 b 中的元素个数相同,且对应元素均相等,则a == b的值为 true,否则值为 false。元素是否相等是用==运算符进行判断的。
  a<b:规则类似于词典中两个单词比较大小,从头到尾依次比较每个元素,如果发生 a 中的元素小于 b 中的元素的情况,则a<b的值为 true;如果没有发生 b 中的元素小于 a 中的元素的情况,且 a 中的元素个数比 b 少,a<b的值也为 true;其他情况下值为 false。元素比较大小是通过<运算符进行的。
  a != b:等价于 !(a == b)。
  a > b:等价于 b < a。
  a <= b:等价于 !(b < a)。
  a >= b:等价于 !(a < b)。

3、成员函数

所有容器都有以下两个成员函数:
  int size():返回容器对象中元素的个数。
  bool empty():判断容器对象是否为空。

顺序容器和关联容器还有以下成员函数:
  begin():返回指向容器中第一个元素的迭代器。
  end():返回指向容器中最后一个元素后面的位置的迭代器。
  rbegin():返回指向容器中最后一个元素的反向迭代器。
  rend():返回指向容器中第一个元素前面的位置的反向迭代器。
  erase(...):从容器中删除一个或几个元素。该函数参数较复杂,此处省略。
  clear():从容器中删除所有元素。

若一个容器是空的,则 begin() 和 end() 的返回值相等,rbegin() 和 rend() 的返回值也相等。
顺序容器还有以下常用成员函数:
  front():返回容器中第一个元素的引用。
  back():返回容器中最后一个元素的引用。
  push_back():在容器末尾增加新元素。
  pop_back():删除容器末尾的元素。
  insert(...):插入一个或多个元素。该函数参数较复杂,此处省略。

4、迭代器简介

  要访问顺序容器和关联容器中的元素,需要通过“迭代器(iterator)”进行。迭代器是一个变量,相当于容器和操纵容器的算法之间的中介。迭代器可以指向容器中的某个元素,通过迭代器就可以读写它指向的元素。

5、分类

迭代器按照定义方式分成以下四种。

5.1 正向迭代器

定义方法如下:

  容器类名::iterator 迭代器名;

5.2 常量正向迭代器

定义方法如下:

  容器类名::const_iterator 迭代器名;

5.3 反向迭代器

定义方法如下:

  容器类名::reverse_iterator 迭代器名;

5.4 常量反向迭代器

定义方法如下:

  容器类名::const_reverse_iterator 迭代器名;

6、用法示例

  通过迭代器可以读取它指向的元素,*迭代器名就表示迭代器指向的元素。通过非常量迭代器还能修改其指向的元素。迭代器都可以进行++操作。反向迭代器和正向迭代器的区别在于:
  对正向迭代器进行++操作时,迭代器会指向容器中的后一个元素;而对反向迭代器进行++操作时,迭代器会指向容器中的前一个元素。
  

  begin 成员函数返回指向容器中第一个元素的迭代器。++i 使得 i 指向容器中的下一个元素。end 成员函数返回的不是指向最后一个元素的迭代器,而是指向最后一个元素后面的位置的迭代器,因此循环的终止条件是i != v.end()。
  对于vector容器而言,也可以使用size函数进行遍历,相比于迭代器会更好。若迭代器指向了容器中最后一个元素的后面或第一个元素的前面,再通过该迭代器访问元素,就有可能导致程序崩溃,这和访问 NULL 或未初始化的指针指向的地方类似。
相比于后置运算符,前置运算符的执行速度更快。

CDemo CDemo::operator++ ()
{ //前置++
  ++n;
  return *this;
}
CDemo CDemo::operator ++(int k)
{ //后置++
  CDemo tmp(*this); //记录修改前的对象
  n++;
  return tmp; //返回修改前的对象
}

  可知,后置++要多生成一个局部对象 tmp,因此执行速度比前置的慢。同理,迭代器是一个对象,STL 在重载迭代器的++运算符时,后置形式也比前置形式慢。因此最好习惯遇到循环控制变量时写成前置。
  注意:容器适配器 stack、queue 和 priority_queue 没有迭代器。容器适配器有一些成员函数,可以用来对元素进行访问。

7、迭代器功能分类

  不同容器的迭代器,其功能强弱有所不同。容器的迭代器的功能强弱,决定了该容器是否支持 STL 中的某种算法。常用的迭代器按功能强弱分为输入、输出、正向、双向、随机访问五种,这里只介绍常用的三种。

4.1 正向迭代器

  假设 p 是一个正向迭代器,则 p 支持以下操作:++p,p++,*p。此外,两个正向迭代器可以互相赋值,还可以用==和!=运算符进行比较。

4.2 双向迭代器

  双向迭代器具有正向迭代器的全部功能。除此之外,若 p 是一个双向迭代器,则--p和p--都是有定义的。--p使得 p 朝和++p相反的方向移动。

4.3 随机访问迭代器

随机访问迭代器具有双向迭代器的全部功能。若 p 是一个随机访问迭代器,i 是一个整型变量或常量,则 p 还支持以下操作:
  p+=i:使得 p 往后移动 i 个元素。
  p-=i:使得 p 往前移动 i 个元素。
  p+i:返回 p 后面第 i 个元素的迭代器。
  p-i:返回 p 前面第 i 个元素的迭代器。
  p[i]:返回 p 后面第 i 个元素的引用。
两个随机访问迭代器 p1、p2 还可以用 <、>、<=、>= 运算符进行比较。
  p1<p2的含义是:p1 经过若干次(至少一次)++操作后,就会等于 p2。其他比较方式的含义与此类似。
对于两个随机访问迭代器 p1、p2,表达式p2-p1也是有定义的,其返回值是 p2 所指向元素和 p1 所指向元素的序号之差(也可以说是 p2 和 p1 之间的元素个数减一)。

容器        迭代器功能
vector       随机访问
deque        随机访问
list         双向
set / multiset   双向
map / multimap   双向
stack       不支持迭代器
queue       不支持迭代器
priority_queue   不支持迭代器

5、迭代器的辅助函数

STL 中有用于操作迭代器的三个函数模板,它们是:
  advance(p, n):使迭代器 p 向前或向后移动 n 个元素。
  distance(p, q):计算两个迭代器之间的距离,即迭代器 p 经过多少次 + + 操作后和迭代器 q 相等。如果调用时 p 已经指向 q 的后面,则这个函数会陷入死循环。
  iter_swap(p, q):用于交换两个迭代器 p、q 指向的值。
需要包含头文件 algorithm。

猜你喜欢

转载自www.cnblogs.com/Sheenagh/p/12217389.html