2-1 C++ 中的STL 简介

本文源自于 C 语言中文网:

前面介绍了 函数模板 和 类模板 两个概念,

C++ 对模板(Template)支持得很好,整个STL(Standard Template Library) 提供了通用的模板类和模板函数;

这些模板类和模板函数就是使用模板把常用的数据结构(如向量、链表、队列、栈)及其算法都实现了一遍,并且做到了数据结构和算法的分离。

自 1998 年 ANSI/ISO C++ 标准正式定案,C++ STL 规范版本正式通过以后,由于各个 C++ 编译器厂商, 在开源版本的基础上,实现了满足自己需求的 C++ STL 泛型库,主要包括 HP STL、SGI STL、STLport、PJ STL、Rouge Wave STL 等。

1. STL的由来

1.1 STL 的性质

STL 最初由惠普实验室开发,于 1998 年被定为国际标准,正式成为 C++ 程序库的重要组成部分。值得一提的是,如今 STL 已完全被内置到支持 C++ 的编译器中,无需额外安装,这可能也是 STL 被广泛使用的原因之一。

STL 就位于各个 C++ 的头文件中,即它并非以二进制代码的形式提供,而是以源代码的形式提供。

从根本上说,STL 是一些容器、算法和其他一些组件的集合,所有容器和算法都是总结了几十年来算法和数据结构的研究成果,汇集了许多计算机专家学者经验的基础上实现的,因此可以说,STL 基本上达到了各种存储方法和相关算法的高度优化。

注意,这里提到的容器,本质上就是封装有数据结构的模板类,例如 list、vector、set、map 等,有关这些容器的具体用法,后续章节会做详细介绍。

1.2 STL 的作用举例

为了让读者清楚地了解 STL 是什么,使用 STL 编程有哪些优势,这里举一个使用 STL 中的向量容器作为一个例子。

以 C++ 定义数组的操作为例,在 C++ 中如果定义一个数组,可以采用如下方式:
int a[n];

这种定义数组的方法需要事先确定好数组的长度,即 n 必须为常量,这意味着,如果在实际应用中无法确定数组长度,则一般会将数组长度设为可能的最大值,但这极有可能导致存储空间的浪费。

所以除此之外,还可以采用在堆空间中动态申请内存的方法,此时长度可以是变量:

int *p = new int[n];

这种定义方式可根据变量 n 动态申请内存,不会出现存储空间浪费的问题。但是,如果程序执行过程中出现空间不足的情况时,则需要加大存储空间,此时需要进行如下操作:
新申请一个较大的内存空间,

即执行int * temp = new int[m];

将原内存空间的数据全部复制到新申请的内存空间中,

即执行memecpy(temp, p, sizeof(int)*n);
将原来的堆空间释放,即执行delete [] p; p = temp;

而为了完成上述的操作,如果采用 STL 标准库,则会简单很多,
因为大多数操作细节将不需要程序员关心。

下面是使用向量模板类 vector 实现以上功能的示例:

//定义 a 数组,当前数组长度为 0,但和普通数组不同的是,此数组 a 可以根据存储数据的数量自动变长。
vector <int> a; 

//向数组 a 中添加 10 个元素
for (int i = 0; i < 10 ; i++)
    a.push_back(i)
    
//还可以手动调整数组 a 的大小
a.resize(100);
a[90] = 100;

//直接删除数组 a 中所有的元素,此时 a 的长度变为 0
a.clear();

//重新调整 a 的大小为 20,并存储 20 个 -1 元素。
a.resize(20, -1)

注意,初学者只需结合注释,大概了解代码功能即可,有关代码中涉及到具体知识,后续会做详细介绍。

对比以上两种使用数组的方式不难看出,使用 STL 可以更加方便灵活地处理数据。所以,大家只需要系统地学习 STL,便可以集中精力去实现程序的功能,而无需再纠结某些细节如何用代码实现。

2. STL的基本组成

2.1 六大基本组件

通常认为,STL 是由容器、算法、迭代器、函数对象、适配器、内存分配器这 6 部分构成;

其中迭代器、函数对象、适配器、内存分配器 是为了更好的使用容器和算法而设计的;

组件含义
容器(Containers)一些封装数据结构的模板类,例如 vector 向量容器、list 列表容器, deque、map 等。
算法(Algorithms)算法作用于容器, 它们提供了执行各种操作的方式,包括对容器内容执行初始化、排序、搜索和转换等操作。STL 提供了非常多(大约 100 个)的数据结构算法,它们都被设计成一个个的模板函数,这些算法在 std 命名空间中定义,其中大部分算法都包含在头文件 < a l g o r i t h m > <algorithm> <algorithm> 中,少部分位于头文件 < n u m e r i c > <numeric> <numeric> 中。
迭代器(iterators在 C++ STL 中,对容器中数据的读和写,是通过迭代器完成的,扮演着容器和算法之间的胶合剂
函数对象如果一个类将 () 运算符重载为成员函数,这个类就称为函数对象类,这个类的对象就是函数对象(又称仿函数)。
适配器可以使一个类的接口(模板的参数)适配成用户指定的形式,从而让原本不能在一起工作的两个类工作在一起。值得一提的是,容器、迭代器和函数都有适配器。
内存分配器为容器类模板提供自定义的内存申请和释放功能,由于往往只有高级用户才有改变内存分配策略的需求,因此内存分配器对于一般用户来说,并不常用。

注:()是目数不限的运算符,因此重载为成员函数时,有多少个参数都可以。

2.2 十三个头文件

另外,在惠普实验室最初发行的版本中,STL 被组织成 48 个头文件;但在 C++ 标准中,它们被重新组织为 13 个头文件,如表 2 所示。

表 2 C++ STL头文件

< i t e r a t o r > <iterator> <iterator> < f u n c t i o n a l > <functional> <functional> < v e c t o r > <vector> <vector> < d e q u e > <deque> <deque> < l i s t > <list> <list>
< q u e u e > <queue> <queue> < s t a c k > <stack> <stack> < s e t > <set> <set> < m a p > <map> <map>
< a l g o r i t h m > <algorithm> <algorithm> < n u m e r i c > <numeric> <numeric> < m e m o r y > <memory> <memory> < u t i l i t y > <utility> <utility>

按照 C++ 标准库的规定,所有标准头文件都不再有扩展名。以 为例,此为无扩展名的形式,而 <vector.h> 为有扩展名的形式。

但是,或许是为了向下兼容,或许是为了内部组织规划,某些 STL 版本同时存储具备扩展名和无扩展名的两份文件(例如 Visual C++ 支持的 Dinkumware 版本同时具备 <vector.h> 和 );甚至有些 STL 版本同时拥有 3 种形式的头文件(例如 SGI 版本同时拥有 、<vector.h> 和 <stl_vector.h>);但也有个别的 STL 版本只存在包含扩展名的头文件(例如 C++ Builder 的 RaugeWare 版本只有 <vector.h>)。

建议读者养成良好的习惯,遵照 C++ 规范,使用无扩展名的头文件。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值