STL 之 容器

来源:互联网 发布:阿里云服务器远程控制 编辑:程序博客网 时间:2024/06/06 15:03

容器概念

    没有与基本容器概念对应的类型,但概念描述了所有容器类都通用的元素。容器概念指定了所有STL容器类都必须满足的一系列要求。

 

    容器是存储其他对象的对象。被存储的对象必须是同一种类型的,他们可以是OOP意义上的对象,也可以是内置类型值。被存储在容器中的数据为容器所有,当容器过期时,存储在容器中的数据也将过期(如果数据是指针的话,则它指向的数据并不一定过期)。

 

    容器存储的类型必须是可复制构造的和可赋值的。基本类型满足这些要求;只要类定义没有将复制构造函数和赋值操作符声明为私有或保护的,则类也满足这种要求。

 

    基本容器不能保证其元素都按特定的顺序存储,也不能保证元素的顺序不变。

 

    所有的容器都提供某些特性和操作,下图对一些通用的特性进行总结。

    其中,X表示容器类型,例如vector;T表示存储在容器中的对象类型;a和b表示类型X的值;u表示类型X的标识符。

 

 

基本的容器特征

 


    “复杂度”描述了执行操作所需的时间。下面由快到慢介绍:

     编译时间,操作将在编译时执行,执行时间为0 。

     固定时间,操作发生在运行阶段,但是独立于对象中的元素数目。

     线性时间,意味着执行时间与元素数目成正比。

 

序列容器

     6种STL容器类型(dequelistqueuepriority_queuestackvector)都是序列(队列能够在队尾添加元素,在队首删除元素。Deque表示的双端队列允许在两端添加和删除元素)。

 

     序列容器保证了元素将按特定的顺序排列,不会在两次迭代之间发生变化。序列还要求其元素按严格的线性顺序排列。

     例如,数组和链表都是序列,但分支结构(其中每个节点都指向两个子节点)不是序列。

 

     下图列出了序列容器所必须具备的要求。其中,X表示容器类型;T表示存储在容器中的对象类型;a和b表示类型X的值;u表示类型X的标识符;t表示T类型的值,n表示整数,p、q、i和j表示迭代器。

 

 

序列的要求

 


     下图列出了某些序列容器还可使用的一些其他操作。他们的复杂度为固定时间。

 

 

序列的可选要求

 

说明:  a[n]和a.at(n)都返回一个指向容器中第n个元素(从0开始编号)的引用。如果n落在容器的有效区间之外,则a.at(n)将执行边界检查,并引发out_of_range异常。

 

6中序列容器类型

1. vector

     该模板是在vector头文件中声明的。简单地说,vector是数组的一种类表示,它提供了自动内存管理的功能,可以动态的改变vector对象的长度,并随着元素的添加和删除增大和缩小。它提供了对元素的随机访问。在尾部添加和删除元素的时间是固定的,但在头部或中间插入和删除元素的复杂度为线性时间。

 

     vector还是可反转容器(reversible container)概念的模型。这增加了两个类方法: rbegin()和rend(),前者返回一个指向反转序列的第一个元素的迭代器,后者返回反转序列的超尾迭代器。

 

     假如dice是一个vector<int>容器,而show(int)是显示一个整数的函数,则下面的代码将首先正向显示dice的内容,然后反向显示:

for_each(dice.begin(),dice.end(),show);    // display in order
cout << endl;
for_each(dice.rbegin(),dice.rend(),show);  // display in reversed order
cout << endl;

 

     这两种方法返回的迭代器都是类级类型reverse_iterator 。对这样的迭代器进行递增,将导致它反向遍历可反转容器。

 

2. deque

     deque模板类(在deque头文件中声明)表示双端队列(double-ended queue),通常被简称为deque。其实现类似于vector容器,支持随机访问。主要区别在于,从deque对象的开始位置插入和删除元素的时间是固定的,而不像vector中那样是线性时间的。

 

3. list

     list模板类(在list头文件中声明)表示双向链表。除了第一个和最后一个元素外,每个元素都与前后的元素相链接,这意味着可以双向遍历链表。

 

     链表的特性使得list强调的是元素的快速插入和删除。

 

     list也是可反转容器。但不支持数组表示法和随机访问。

 

     从该容器中插入或删除元素之后,链表迭代器指向元素将不变。例如,假设有一个指向vector容器第5个元素的迭代器,并在容器的起始处插入一个元素。此时,必须移动其他所有元素,以便腾出位置。因此插入后,第5个元素包含的值将是以前第4个元素的值。因此,迭代器指定的位置不变,但数据不同。


     然而,在链表中插入新元素并不会移动已有的元素,而只是修改链接信息。指向某个元素的迭代器仍然指向该元素,但它链接的元素可能与以前不同。

 

     除了序列和可反转容器的函数外,list模板类还包含了链表专用的成员函数。下图列出了其中一些。通常不必担心Alloc模板参数,因为它有默认值。


list成员函数


4. queue

     queue模板类(在queue(以前为queue.h)头文件中声明)是一个适配器。比如ostream_iterator模板就是一个适配器,让输出流能够使用迭代器接口。同样,queue模板让底层类(默认为deque)展示典型的队列接口。

 

     queue模板类不允许随机访问队列元素,不允许遍历队列。它把使用限制在定义队列的基本操作上,可以将元素添加到队尾、从队首删除元素、查看队首和队尾的值、检查元素数目和测试队列是否为空。

 

     下图列出了这些操作。

 


queue的操作

 


5. priority_queue

     priority_queue模板类(在queue头文件中声明)是另一个适配器类,它支持的操作与queue相同。两者的主要区别在于,在priority_queue中,最大的元素被移动到队首。内部区别在于,默认的底层类是vector 。

 

     可以修改用于确定哪个元素放到队首的比较方式,方法是提供一个可选的构造函数参数:

     priority_queue<int> pq1;                         // default version
     priority_queue<int> pq2 (greater<int>);
// use greater<int> to order
     greater<>() 函数是一个预定义的函数对象。

 

6. stack

     stack模板类(在stack(以前为stack.h)头文件中声明)是一个适配器类,它给底层类(默认为vector)提供了典型的堆栈接口。

 

     stack模板类不允许随机访问堆栈元素,不允许遍历堆栈。它把使用限制在定义堆栈的基本操作上,可以将压入推到栈顶、从栈顶弹出元素、查看栈顶的值、检查元素数目和测试堆栈是否为空。

 

     下图列出了这些操作。

 


stack的操作

 


联合容器

      联合容器(associative container)将值与关键字关联在一起,使用关键字来查找值。

 

      例如,值可以是表示雇员信息(如姓名、地址、办公室号码等等)的结构,关键字可以是具有唯一性的员工编号。为了获取雇员信息,程序将使用关键字查找雇员结构。


      对于容器X,表达式X::value_type通常指出了存储在容器中的值的类型。对于联合容器来说,表达式X::key_type指出了作用关键字的类型。

 

      联合容器提供了对元素的快速访问。与序列相似,联合容器也允许插入新元素,不过不能指定元素的插入位置。因为联合容器通常包含用于确定数据放置位置的算法,以便能够很快检索信息。

 

4种联合容器

      selmultiset是在set头文件中(以前分别为set.h和multiset.h)定义的。
      mapmultimap是在map头文件中(以前分别为map.h和multimap.h)定义的。

 

      set其值的类型与关键字相同,关键字是唯一的——集合中不会有多个相同的关键字。对于set来说,值就是关键字。

      详细介绍请查看<<STL 之 set联合容器范例 >>。


      multiset类似于set,但该类型可能有多个值的关键字相同。

 

      map其值的类型与关键字不同,关键字是唯一的,每个关键字只对应一个值。


      multimap类似于map,只是一个关键字可以与多个值关联。

      详细介绍请查看<<STL 之 multimap联合容器范例 >>。

原创粉丝点击