8
库概述
如果只是临时露个小怯,
还费那劲学它干嘛呢?
—— 霍布斯虎1
8.1 导言
任何重要的程序都不能单纯使用基本的编程语言。首先,要开发一套程序库。 这些库会构成后续工作的基石。 如果仅使用基本的编程语言编写,绝大多数程序都很烦冗, 相反,采用良好的程序库,几乎任何业务都能化繁为简。
继1-7章之后,9-15章对主要的标准库部件作了概览。
我很简略地介绍了实用的标准库类型,比如string
、ostream
、variant
、
vector
、map
、path
、unique_ptr
、thread
、regex
和complex
,
及其基本用法。
像1-7章一样,对部分细节无法完全理解的时候,千万别分神、灰心。 本章的目的是:基本理解最实用的标准库部件。
标准库的内容占据了 ISO C++ 标准超过三分之二。 请浏览并尽量采用它,摒弃山寨货。 许多思想注入了它的设计当中,更多的则进入了实现, 还有许多劳作将投入到它的维护和扩展中去。
本书所描述的标准库部件,是每个完整C++实现的组成部分。 除这些标准库组件以外,多数实现还提供了 “图形用户接口(graphical user interface)”系统(GUIs)、 Web接口、数据库接口等等。 类似地,多数应用开发环境还为企业级或工业级“标准”的开发 和/或 运行环境提供了“基本库”。 在此,我不会讲述那些系统和库。
而旨在为标准定义下的C++提供一个自洽的描述,并确保示例可移植。 当然,也鼓励程序员去浏览各个系统提供的范畴广泛的基础部件。
8.2 标准库组件
标准库提供的基础部件可按此分类:
- 运行时语言支持(即:用于资源分配以及提供运行时类型信息)。
- C 标准库(带有极细微的调整,以便降低与类型系统的冲突)。
- 字符串(对国际化字符集、本地化以及子字符串的只读检视提供支持);参见 §9.2。
- 对正则表达式匹配的支持;参见 §9.4。
- I/O 流,是一个针对输入输出的可扩展框架,用户可添加自定义的类型、流、缓冲策略、 地区和字符集(第10章)。还有个文件系统库,以可移植方式操作文件(§10.10)。
- 容器(例如
vector
和map
)框架和算法 (例如find()
、sort)
和merge()
);参见第11章和第12章。 习惯上,该框架被称为 STL[Stepanov,1994],对用户而言是可扩展的, 可添加自定义容器和算法。 - 对数值计算的支持(例如标准的数学函数、复数、带算术运算的向量和随机数生成器); 参见 §4.2.1 和第14章。
- 对并发运算的支持,包括
thread
和锁;参见第15章。 对并发的支持非常基础,因此用户能够以库的形式,为并发的新模型添加支持。 - 多数STL算法以及部分数值算法的并行版本(即
sort()
和reduce()
); 参见 §12.9 和 §14.3.1。 - 用于支持模板元编程的实用工具(即类型trait;§13.9),
STL风格的泛型编程(即
pair
;§13.4.3),通用编程 (即variant
和optional
;§13.5.1,§13.5.2)和clock
(§13.7)。 - 对高效且安全的通用资源管理提供支持,外加一个可选的垃圾回收器接口(§5.3)。
- 针对资源管理的“智能指针(smart pointers)”
(即
unique_ptr
和shared_ptr
,§13.2.1)。 - 特殊用途的容器,例如:
array
(§13.4.1)、bitset
(§13.4.2) 和tuple
(§13.4.3)。 - 常见单位的后缀,例如:毫秒(millisecond)的
ms
和 虚部(imaginary)的i
(§15.4.4)。
某个类被纳入库中的判断标准是:
- 它能给几乎所有C++程序员(不论新手还是专家)带来益处,
- 相对于该部件的简单版本而言,通用版本不会显著增加负担,并且
- 基本用法应该易于学习(相较其功能固有的复杂性而言)。
基本上,C++标准库提供了最常见的基础数据结构,以及应用其上的基本算法。
8.3 标准库头文件和命名空间
每个标准库部件都通过某些头文件提供。例如:
#include<string>
#include<list>
这样就可以使用标准的string
和list
了。
标准库被定义在一个名为std
的命名空间(§3.4)里。
要使用标准库部件,可借助前缀std::
:
std::string sheep {"Four legs Good; two legs Baaad!"};
std::list<std::string> slogans {"War is Peace", "Freedom is Slavery", "Ignorance is Strength"};
出于简洁的原因,本书极少像例中那样显式使用std::
,
也不总显式#include
必要的头文件。
要编译并运行这个代码片段,必须#include
对应的头文件并确保其声明的名称可访问。
例如:
#include<string> // make the standard string facilities accessible
using namespace std; // make std names available without std:: prefix
string s {"C++ is a general−purpose programming language"}; // OK: string is std::string
一般来说,把某个命名空间里的所有名称一股脑丢进全局这种做法,颇有不妥。 但在此书专注于使用标准库,而熟知其涵盖的内容颇为有益。
以下是标准库头文件的一个精选,其中所有声明在命名空间std
里:
精选标准库头文件 | ||
---|---|---|
<algorithm> |
copy() ,find() ,sort() |
第12章 |
<array> |
array |
§13.4.1 |
<chrono> |
duration ,time_point |
§13.7 |
<cmath> |
sqrt() ,pow() |
§14.2 |
<complex> |
complex ,sqrt() ,pow() |
§14.2 |
<filesystem> |
path |
§10.10 |
<forward_list> |
forward_list |
§11.6 |
<fstream> |
fstream ,ifstream ,ofstream |
§10.7 |
<future> |
future ,promise |
§15.7 |
<ios> |
hex ,dec ,secientific ,fixed ,defaultfloat |
§10.6 |
<iostream> |
istream ,ostream ,cin ,cout |
第10章 |
<map> |
map ,multimap |
§11.5 |
<memory> |
unique_ptr ,shared_ptr ,allocator |
§13.2.1 |
<random> |
default_random_engine ,normal_distribution |
§14.5 |
<regex> |
regex ,smatch |
§9.4 |
<string> |
string ,basic_string |
§9.2 |
<set> |
set ,multiset |
§11.6 |
<sstream> |
istringstream ,ostringstream |
§10.8 |
精选标准库头文件(续) | ||
---|---|---|
<stdexcept> |
length_error ,out_of_range ,runtime_error |
§3.5.1 |
<thread> |
thread |
§15.2 |
<unordered_map> |
unordered_map ,unordered_multimap |
§11.5 |
<utility> |
move() ,swap() ,pair |
第13章 |
<variant> |
variant |
§13.5.1 |
<vector> |
vector |
§11.2 |
此列表远称不上面面俱到。
C标准库的头文件,例如stdlib.h
也都在。
每个这样的头文件还有个改版,加了c
前缀,移除了.h
。
这样的版本,比如<cstdlib>
把它的声明都放进了命名空间std
。
8.4 忠告
- [1] 别重复造轮子,用库;§8.1; [CG: SL.1]。
- [2] 面临选择的时候,相较于其它的库,优先选标准库;§8.1; [CG: SL.2]。
- [3] 别盲目觉得标准库通吃一切;§8.1。
- [4] 针对使用的部件,记得
#include
其头文件;§8.3。 - [5] 记住,标准库部件定义在命名空间
std
里;§8.3; [CG: SL.3]。
1. 出自连环漫画集“The Complete Calvin and Hobbes”其中一册叫“Attack of the Deranged Mutant Killer Monster Snow Goons”,中文版为《卡尔文与霍布斯虎》系列丛书的第三册《变种雪怪杀手》,其中把此句译为“如果无知是瞬时的,为什么还要花时间学习呢?”,译者结合该小故事的上下文,认为该如此翻译。 ↩