C++标准库的学习笔记
C++标准库是个极为庞大的体系,因此面面俱到的介绍几乎是不可能的。而且任何一个系统都有其复杂的一面,我觉得即使是只介绍 C 的标准库其工作量也是巨大的,所以我这次整理的内容只也是从宏观入手。介绍 C++标准库相关细节的文章(或书籍)有不少,不同的文章也有各自的侧重点。我觉得了解C++标准库有助于加深对 C++ 的理解,因此我想整理一下自己的学习心得,也可以使大家从另一个角度认识一下 C++。因为我曾经看到有位版主也说过 string 类的底层是 vector<char> 这么不靠谱的话,所以才觉得介绍介绍这些方面的知识也挺有意义。语法并不是一个语言的全部,学习一门语言的精力经常是放在学习它的库上,或是内置库,或是扩展库,第三方库。不过精力应该放在学习库的用法上,而不是掌握库的结构或实现,所以我这篇文章准备谈及的内容也比较偏,不过依然有可能激发一些人的学习兴趣。大家应该了解,语法像 C++ 这般复杂的语言是绝无仅有的,很多简单的语言,只要愿意,半小时就能学尽它的语法,并且这种语言依然很有效。(对于熟悉 Linux 的人来说,一个很好的例子是 bash,即使它对 sh 的语法做了如此之多的扩展,你仍能在半小时之内掌握它的全部语法。)
我这么说的意图是,即使通晓C++的语法(先不论是否能够通晓)也许也无法理解它的设计哲学。标准库也是 C++ 的一个很重要的组成部分,这是我觉得这篇文章确实值得一写的另一个原因。
标准库的组成:
前言就到此为止。从最宏观的层面上看,C++标准库由十个部分组成:语言支持、诊断、通用工具、字符串、本地化、容器、迭代器、通用算法、数值算法和I/O。相信其中有很多是大家已经熟知的了,我也不会详细地介绍每个部分。关于容器、迭代器和算法相关的细节与原理,我认为参考侯捷老师的《STL源码剖析》是非常适宜的。虽然其中也有少量的错误的,但无法掩盖它的价值。相信我的这篇文章错误会更多,因为我对 C++ 确实知之甚少。
名空间std:
所有的标准库定义的东西,除了宏、operator new 和 operator delete(马上我就会解释宏,而 new 和 delete 我将在介绍语言支持的时候介绍)都在 std,或者是内嵌在 std 里的名空间里面。
(也许大家不熟悉这种语法。下面的例子定义了两个名空间 outer 和 inner。inner 就是内嵌在 outer 里的。它们都独立于 std:
程序代码:
#include <iostream> using std::cout; using std::endl; namespace outer { int a = 5; namespace inner { int a = 10; } } using namesapec outer; int main() { cout << a << endl; // 输出:5 cout << inner::a << endl; // 输出:10 return 0; }
)
头文件组成:
C++ 标准库一共包含 33 个C++ 头文件:
<algorithm>, <bitset>, <complex>, <deque>, <exception>, <fstream>, <functional>,
<iomanip>, <ios>, <iosfwd>, <iostream>, <istream>, <iterator>, <limits>, <list>,
<locale>, <map>, <memory>, <new>, <numeric>, <ostream>, <queue>, <set>, <sstream>,
<stack>, <stdexcept>, <streambuf>, <string>, <strstream>, <typeinfo>, <utility>,
<valarray>, <vector>
和 18 个 C的标准库头文件:<iomanip>, <ios>, <iosfwd>, <iostream>, <istream>, <iterator>, <limits>, <list>,
<locale>, <map>, <memory>, <new>, <numeric>, <ostream>, <queue>, <set>, <sstream>,
<stack>, <stdexcept>, <streambuf>, <string>, <strstream>, <typeinfo>, <utility>,
<valarray>, <vector>
<cassert>, <cctype>, <cerrno>, <cfloat>, <ciso646>, <climits>, <clocale>, <cmath>,
<csetjmp>, <csignal>, <cstdarg>, <cstddef>, <cstdio>, <cstdlib>, <cstring>, <ctime>,
<cwchar>, <cwctype>
再次强调,所有的头文件都不以 .h 结尾。且除了宏以外,都定义在名空间std 里。宏是预处理对象,它不在名空间里是件很自然的事情,因为对宏的处理发生在编译之前。但这经常引起混淆,比如 errno (在 <cerrno> 里)就是一个宏。如果以为它是一个 int,从而使用 std::errno 这样的语句,那么就会出错,而且往往编译器报的错误看上去还非常古怪。另外有一些看上去像函数的宏也常常引发问题,不过这个概率要小一些,因为在 C 的实践中,它们也老出问题,从而累积了不少经验。<csetjmp>, <csignal>, <cstdarg>, <cstddef>, <cstdio>, <cstdlib>, <cstring>, <ctime>,
<cwchar>, <cwctype>
以下划线开始的标识符:
标准规定,以双下划线开始(如 __USE_GNU)或者以单下划线开始并跟一个大写字母(如_M_gcount,这两个标识符出自 gcc)的名字是保留给实现的(也就是说标准库里不用这样的名字);任何以单下划线开始的名字是保留给实现和标准库的。所以我们应当避免自己定义这样的名字,这样可以使阅读代码的人很清楚,一但下划线出现,必然是在引用标准库或者实现的特性。
库函数的可重入性:
标准并没有规定 C++ 的库函数是否是可重入的,这就需要程序员自己斟酌可能出现的情况。
OK,对标准库的概述就是这样,是不是感觉也很简单?我希望我可以做到深入浅出。当然这只是个开始,我马上准备介绍的是语言支持。如果有时间,我还准备陆续介绍一下标准库的那其它组成部分。我很乐意看到大家提出意见或建议。
[ 本帖最后由 pangding 于 2012-4-3 09:14 编辑 ]