C++/C学习笔记(九)

                                              ——学习和使用STL

1.STL简介

STL(Standard Template Library)是C++标准库的最主要和最重要的组成部分。STL是一个标准规范,只是为容器、迭代器和泛型算法等组件定义了一整套统一的上层访问接口及各种组件搭配运用的一般规则,而没有定义组件底层的具体实现方法,因此不同的软件供应商都可以提供自己的STL实现版本,如Microsoft、Borland、HP、SGI等,虽然这些版本之间可能存在某些差异,但是概念和接口都是一致的。我们可以把STL看做是一个概念模型库或者是一个组件架构。

STL主要班阔下面这些组件:I/O流、string类、容器类(Container)、迭代器(Iterator)、存储分配器(Allocator)、适配器(Adapter)、函数对象(Functor)、泛型算法(Algorithm)、数值运算、国际化和本地化支持,以及标准异常类等。最主要的“六大组件”为:容器、存储分配器、迭代器、泛型算法、函数对象、适配器。容器类相当于数据结构,算法用于操作容器中的数据,而迭代器则是它们之间的一个桥梁。

这些组件的相互关系如图所示:

2.STL头文件的分布

STL源代码头文件一般存放在开发环境的include目录下。STL组件都被纳入了名字空间std::,所以在使用其中的组件之前需使用

using声明或using指令,或者也可以在每一处都直接使用完全限定名std::。

(1)容器类

容器类定义在下表所示的头文件中。关联式容器multimap和multiset也都分别定义在<map>和<set>中,hash_multimap和hash_multiset定义在<hash_map>和<hash_set>中。

STL容器的头文件

头文件

内    容

<vector>

元素类型为T的向量,包括了特化vector<bool>

<list>

元素类型为T的双向列表

<deque>

元素类型为T的双端队列

<queue>

元素类型为T的普通队列,包括了priority_queue

<stack>

元素类型为T的堆栈

<map>

元素类型为T的映射

<set>

元素类型为T的集合

<bitset>

布尔值的集合(实际不是真正意义上的集合)

<hash_map>

元素类型为T的hash映射

<hash_set>

元素类型为T的hash集合

(2)泛型算法

只要是由一系列元素构成的结构原则上都可以应用泛型算法,像C++/C数组、字符串、I/O流等特殊的容器也可以使用某些泛型算法———它们定义在头文件<algorithm>和<utility>中。

(3)迭代器

迭代器就是用来遍历元素序列或元素集合的“通用指针”,但是每一种容器都定义了适合自己的迭代器,那些具有特殊功能的迭代器,如输入/输出迭代器、插入迭代器、反向迭代器等都是迭代器适配器,定义在头文件<iterator>中。

(4)数学运算库

STL有几个专门为数学运算设计的类和算法,定义在下表所示头文件中。

STL数学运算库

头文件

内容

<complex>

复数及相关操作

<valarray>

数值向量及相关操作

<numerics>

通用数学运算

<limits>

常用数值类型的极限值和精度等

(5)通用工具

下表定义了在STL容器和泛型算法中用到的辅助组件,有标准的函数对象、pair<>、auto_ptr<>类等。

STL通用工具

头文件

内容

<utility>

运算符重载和pair<>定义

<functional>

标准的函数对象及其便捷函数定义

<memory>

存储分配器和auto_ptr<>类

(6)其他头文件

除以上头文件外,还有一些经常使用的组件,分别定义在下面这些头文件中:<typeinfo>、<stdexcept>、<strsteam>、<string>、<istream>、<ostream>、<iostream>、<new>、<iomanip>、<fstream>等等。

3.容器设计原理

(1)内存映像

容器就是能够容纳其他对象作为其元素的对象,例如数学中的集合就是一种容器概念。由于容器在概念上是一种可以动态增大和减小的模型,所以其元素对象在实现上不可能直接保存在容器对象里面,而应该保存在自由内存(Free Memory)或堆(Heap)上。要区分两个概念:“容器对象”和“容器元素对象”。容器本身就是一个C++类的对象,其大小在运行时不可能改变,因此容器必须有办法指示其每一个元素对象在内存中的位置,以便用户能够通过容器对象找到其中的元素对象。

先介绍一个典型的容器std::vector<T>的内存映像,来展示C++中STL是如何实现容器的。

在C++中,我们使用信息隐藏和封装技术把数据隐藏在类内部不许外部直接操作,同时提供访问器(如get_xxx成员函数)和修改器(如set_xxx成员函数)。STL容器的设计原理如出一辙,只是它们在实现时考虑的问题更多、更复杂而已。容器不仅把元素对象隐藏了起来,而且把元素对象的内存申请和释放操作也全部隐藏了起来(通过存储分配器),这就使程序员彻底摆脱了直接操纵底层内存指针的麻烦,也避免了危险。

(2)存储方式和访问方式

向量(vector)和链表(linked list)是两种最基本的动态结构,也是STL中两种最基本的容器,分别对应动态数组和链表结构,同时它们分别代表了内存中同类型批量数据存放的两种基本方式:连续存储和随机存储。

不同的存储方式决定了元素的不同访问方式,即随机访问和顺序访问。所谓随机访问就是指可以直接通过开销恒定的算术运算来得到任一元素的内存地址的访问方法,而无法直接得到任一中间元素对象的地址。C++/C的内置数组和vector都是既可以随机访问又可以顺序访问的容器,而list则只能顺序访问。

只要底层存储机制采取连续存储方式的容器,就可以随机访问其中任一元素对象,否则只能顺序访问;而任何容器都可以顺序访问,即遍历。

(3)顺序容器和关联式容器的比较

这里的“顺序”和“关联”指的是上层接口表现出来的访问方式,并非底层存储方式。顺序容器主要采用向量和链表及其组合作为基本存储结构,如堆栈和各种队列;而关联式容器采用平衡二叉搜索树作为底层存储结构。

链表(list)作为顺序容器的典型代表,其特点就是“天然有序(Sequential)”,即各元素之间具有天然的相对位置关系,这就决定了理论上它可以存储任意多个元素,并且不需要对所有元素按值的大小进行排序。

集合(set)是关联式容器的典型代表,需要从两个层次来理解:概念模型和实现方式。集合在概念上是一种无序的容器,这不仅指其中的各个元素之间没有相对位置关系,而且元素不需要按值的大小排序。但是如果要在计算机上实现这样的模型,不但要使上层访问接口正确反映集合的概念,还要考虑实现的效率。因此关联式容器在实现上必须是有序的和排列的,只不过要对用户透明(不让用户知道)。平衡二叉搜索树就能够满足这些要求。

由于关联式容器在概念上是无序的,所以它只能通过元素的值来定位其中的元素对象,这就是关联式容器具有find()函数的原因,也是调用这种容器的insert()函数和erase()函数时可以不指定插入位置和删除位置而仅指定元素值或者索引值的原因(关联式容器会自动地为新插入的元素安排一个合适的位置)。

由于顺序容器本来就有“序”,所以它是通过元素对象在容器中的位置来标识一个元素的,而不是通过元素的值(因为它可以存储值相等的多个元素对象,而且它们的位置不一定相邻)。这也就是调用顺序容器的insert()函数和erase()函数时必须指定插入位置和删除位置而不能仅指定元素值的原因。当然,关联式容器也能存储值相等的元素,比如multimap和multiset等,但是它们在容器中的位置肯定是相邻的。

映射(map)是一种独特的关联式容器,它是一个二维的索引表,其每一个元素是一个<关键字,值>的二元组合,即pair<Key,Value>。这就是说,set是Key与自己关联,而map则是Key与Value关联。显然,map应该提供通过Key值来定位Value对象的接口,这就是map具有operator[]运算符函数的原因。

如果经常进行的操作是元素查找,应使用关联式容器;经常插入和删除,则应用顺序容器。

关联式容器在实现上必须维持元素的有序性,因此不能再企图修改已经插入到其中的元素对象,否则就会破坏这种有序性,从而影响性能和正确性。为此,set把iterator强制声明为const_iterator来达到修改的目的,map则把pair中的Key强制声明为const Key,map仅允许修改pair中的Value。这也就是说,你无法直接为关联式容器的元素对象赋予新值或者调用它们的non-const方法。相反,顺序容器允许修改元素对象的值,或者直接赋值,或者调用non-const方法。

(4)遍历容器

STL容器范围表示为“前闭后开”区间法,即[first,last],其中迭代器last要么指向最后一个有效元素的末尾,要么指向一个空白节点,反正不是指向最后一个元素。

遍历容器较好的方法见下例:(把Container替换为具体的泛型容器类,theContainerObj替换为该容器类的对象即可)

/*******************************************************

for(Container::iterator first=theContainerObj.begin(),

last=theContainerObj.end();

first!=last;

++first)//或者使用反向迭代器rbegin()和rend()

{

cout<<first->...<<endl;

cout<<(*first)...<<endl;

...

}

template<typename InputIterator,typename T>

InputIterator find(InputIterator first,InputIterator last,const T& value)

{

while(first!=last&&first!=value) ++first;

return first;

}

/*****************************************************************

(5)存储空间重分配问题

存储空间重分配问题起源于容器元素对象的动态创建和连续存储特性,因此只有连续存储的容器才可能需要运行时的存储空间重分配,典型如vector,deque。

对于一个已经存在的vector,其元素被保存在地址连续的存储空间上,当向该元素vector中插入一个新元素时,必须保证新的容器仍然满足元素连续存储的条件,这就要求扩张原有容器的空间。若当前vector容器没有那么多空余容量,则必须要给整个vector的所有元素重新分配存储空间,把所有元素拷贝到新地址处,释放原来的存储空间,最后将原来的容器对象调整为指向新的内存位置。

若当前vector容器有足够的空余容量来容纳待插入的新元素,此时不需要重新分配存储空间,但不是在尾部进行插入操作的话,同样需要移动vector的部分元素和调整指针。

因此要尽量在容器的尾部执行插入操作,效率最高。对于顺序容器来说,它们本身并不要求元素排序,因此完全没必要在开头或中间插入元素;对于关联式容器,它们在实现时就是有序的,即调用insert()的时候会自动进行重新排序。

转载于:https://www.cnblogs.com/yingying0907/archive/2012/08/17/2644137.html

C++/C学习笔记(九)相关推荐

  1. IOS学习笔记(九)之UIAlertView(警告视图)和UIActionSheet(操作表视图)基本概念和使用方法...

    IOS学习笔记(九)之UIAlertView(警告视图)和UIActionSheet(操作表视图)基本概念和使用方法 Author:hmjiangqq Email:jiangqqlmj@163.com ...

  2. python3.4学习笔记(九) Python GUI桌面应用开发工具选择

    python3.4学习笔记(九) Python GUI桌面应用开发工具选择 Python GUI开发工具选择 - WEB开发者 http://www.admin10000.com/document/9 ...

  3. 吴恩达《机器学习》学习笔记九——神经网络相关(1)

    吴恩达<机器学习>学习笔记九--神经网络相关(1) 一. 非线性假设的问题 二. 神经网络相关知识 1.神经网络的大致历史 2.神经网络的表示 3.前向传播:向量化表示 三. 例子与直觉理 ...

  4. ROS学习笔记九:用C++编写ROS发布与订阅

    ROS学习笔记九:用C++编写ROS发布与订阅 本节主要介绍如何用C++编写一个简单的ROS发布与订阅. 编写发布节点 在之前创建的例子beginner_tutorials软件包中,在其目录下的src ...

  5. at24c16如何划分出多个读写区_AVR学习笔记九、基于AT24C16的数据存储实验

    Ema{@AVR 学习笔记九.基于 AT24C16 的数据存储实验 ------- 基于 LT_Mini_M16 9.1 用 I/O 口模拟 I2C 总线实现 AT24C16 的读写 9.1.1 .实 ...

  6. JavaScript学习笔记(九)(验证框架,layer弹出层)

    JavaScript学习笔记(九) 一.jQuery Validate验证框架 1.引入相关插件路径 2. 修改一些规则 3. 自定义验证规则 4.异步验证 整体代码 二.layer弹出层 1.引入相 ...

  7. OpenCV学习笔记(九)——图像轮廓(下)

    <OpenCV轻松入门:面向Python>学习笔记(九) 1-3 查找并绘制轮廓.矩特性及Hu矩 4-5 轮廓拟合及凸包 6. 利用形状场景算法比较轮廓 6.1 计算形状场景距离 6.2 ...

  8. Polyworks脚本开发学习笔记(九)-公差控制及制作报告

    Polyworks脚本开发学习笔记(九)-公差控制及制作报告 定义公差 系统有默认的公差设置,可以在选项中进行系统的默认设置,但往往不是想要的.比如下图的XYZ三向都是-/+1,我想只控制Y向并且公差 ...

  9. 步步为营 .NET 代码重构学习笔记 九

    步步为营 .NET 代码重构学习笔记系列 步步为营 .NET 代码重构学习笔记 一.为何要代码重构 步步为营 .NET 代码重构学习笔记 二.提炼方法(Extract Method) 步步为营 .NE ...

  10. [傅里叶变换及其应用学习笔记] 九. 继续卷积的讨论

    这份是本人的学习笔记,课程为网易公开课上的斯坦福大学公开课:傅里叶变换及其应用. 卷积在滤波中的应用 浑浊度(Turbidity)研究是关于测量水的清澈度的研究.大致方法是把光传感器放置到深水区域,然 ...

最新文章

  1. 关于时间类型数据的转换
  2. OpenCV安装好了,show第一个例子,找找成就感~~~
  3. 磁盘格式化与快速格式化有什么区别?
  4. 就掌握了成功的C++,那么信息从何而来
  5. 数据结构:单链表和双向链表
  6. 测试学python路线_小蜗分享:Python测试开发全套学习路线
  7. 一加7 Pro将首发搭载UFS 3.0闪存:较UFS2.1闪存提升明显
  8. c语言编译器储存有什么用,C编译器怎么样对内存划分和使用
  9. POJ-2152 Fire (树形DP)
  10. 《C语言入门经典》读后感(一)
  11. SQL 基础教程:SQL 通配符
  12. Java 生成N位随机数的方法
  13. python 正则表达式 sub_Python 正则表达式:sub
  14. 生物特征模板保护技术
  15. ubuntu16.04 配置远程桌面
  16. 2019CVPR单目深度估计综述
  17. 为什么alert没有弹窗?
  18. 使用synchronized实现Lock接口的lock和unlock方法
  19. 罗塞塔石碑(Rosetta Stone)安装指南
  20. cps1 cps2 android,CPS1和CPS2模拟器详细图文教程

热门文章

  1. 可靠性测试设备技术含量_品牌手机厂商为何如此重视可靠性测试,海拓仪器一语道破玄机...
  2. 计算机小故障排除方法,常见电脑故障排除方法总汇【详解】
  3. Java虚拟机(十二)——StringTable
  4. 千万级别数据查询优化_从千万级数据查询来聊一聊索引结构和数据库原理
  5. linux 1060显卡,Steam最受欢迎显卡型号出炉:GTX 1060显卡的占有率依然排名第一
  6. php是fast_cgi模式,cpanel中的php运行方式使用fast-cgi模式的配置方法
  7. 哪吒之魔童降世视听语言影评_豆瓣评分8.7,这个“新哪吒”不一般|《哪吒之魔童降世》影评...
  8. python决策树原理_关于决策树算法最通俗的一次Python学习教程,你肯定得看
  9. 计算机应用技术概括,计算机应用技术学科各学科方向简介
  10. 中移4G模块-ML302-OpenCpu开发-(MQTT连接阿里云-RRPC通讯)