Effective C++ 49,50
最后更新于:2022-04-01 15:50:05
49.熟悉标准库。
C++标准库很大。
首先标准库中函数很多,为了避免名字冲突,使用命名空间std。而之前的库函数都存放于< .h>中,现在成为伪标准库。而不能直接将这些头文件全部直接添加命名空间,标准委员会只能重新创建了不带.h的头文件。对于C中头文件采用同样的方法,但是每个名字前添加一个c,如C中的<string.h>变成了<cstring>。旧的c++头文件是官方反对使用的,但旧的c头文件不是,为了保持对C的兼容性。如 <string.h>是旧的C头文件,对应的是基于char*的字符串处理函数,<string>是包含了std的C++头文件,对应的是新的string类,而<cstring>是C头文件的std版本,但这里没有string类的旧c++版本,因为官方不推荐这样做。
第二,库中几乎都是模版。如iostream,操作字符流,流类实际就是类模版,在实例化流类的时候指定字符类型。即使是string,其实也是一个模版,类型参数限定了每个string类中的字符类型。string的类型声明是:
~~~
typedef basic_string<char, char_traits<char>, allocator<char> >
string;
~~~
这里的字符类型与之前iostream中的字符类型是一个意思,它不是指char,指的是一个流中的字符,这里字符类型确定其参数,字符类型指的是字符集,因为不同的字符集在实现的细节上不同,如特殊的文件结束字符,拷贝他们的数组的最有效方式等,这些特征在标准中被称为traits。然后还有string对象执行动态内存分配的方法,使用一个Allocator参数,而Allocator类型的对象被用来分配和释放string对象的内存,其是一个内存管理器。不要手动的声明标准库中的任何部分。
Iostream,和传统的Iostream相比,它已经被模版化,继承层次结构也进行了修改,增加了抛出异常的能力,支持string(通过stringstream)和国际化(通过locales)。新的Iostream可以将string和文件当作流,还可以对流的行为做更广泛的控制,包括缓存和初始化。
容器,标准库中提供了一下的高效实现,vector,list,queue,stack,deque,map,set和bitset。string是容器,对容器的任何操作都适用于string。标准库的实现是高效的。使用容器可以消除动态分配内存造成的内存泄漏。
算法,标准库中提供了大量的简易方法,称为algorithm,实际为函数模版,其中大多数适用于库中所有容器以及内建数组。算法将容器的内容作为序列,每个算法可以应用与一个容器中所有值对应的一个序列,或者一个子序列。标准算法有,for_each 为序列中的每个元素调用某个函数, find 在序列中查找包含某个值的第一个位置, count_if 计算序列中满足某个判定为真的元素的数量,equal 判断两个序列包含的元素的值是否完全相等,search 在一个序列中找出某个子序列的起始位置,copy 拷贝一个序列到另一个, unique 在序列中删除重复值, rotate 旋转序列中的值,sort 对序列中的值排序,等等。和容器操作一样,算法也有性能保证。
对国际化的支持。提供有助于开发出国际化软件的特性。支持国际化的最主要的构件是facets 和 locales 。facets描述的是对一种文化要处理哪些特性,包括排序规则(即某些地区字符集中的字符应该如何排序),日期和时间应该如何表示,数字和货币值应该如何表示,怎样将信息表示符映射成明确的语言信息,等等。locales 将多组facets捆绑在一起,facets是指前面说到的那些特性中的一个,而locales 表示多个facets组成的一个对于某个国家的规则,若一个locales表示美国人是如何解决前面几个问题的。
对于数字处理的支持。C++库中为复数类和专门针对数值编程而设计的特殊数组提供了模版。如valarray 类型的对象可以用来保存可以任意混叠的元素,
诊断支持,标准库支持三种报错方式:C的断言,错误号,例外 exception 。例外先派生出 logic_error 和 runtime_error ,然后再有这两个类派生出具体的错误类型,logic_error 表示软件中的逻辑错误,理论上可以通过更仔细的程序设计来防止。runtime_error 类型的例外为运行时才能发现的错误。
标准库中的容器和算法这部分一般被称为标准模版库 STL 。STL是标准库中最具创新的部分,它的体系结构具有扩展性,按照STL中的规范,可以进行很多扩展。
50.提高对C++的认识。
C++设计时的首要目标:与C的兼容性,效率,和传统开发工具及环境的兼容性,解决真实问题的可应用性(即面向对象)。
以上目标阐明了C++语言中大量的实现细节。如,为什么隐式生成的拷贝构造函数和赋值运算符要像现在这样工作,尤其是指针产生的浅拷贝问题?因为这是C对struct的拷贝和赋值的方式,要与C兼容。为什么析构函数不自动声明为virtual,为什么实现细节必须出现在类的定义中?因为不这样做会带来性能上的损失,效率很重要。为什么C++不能检测非局部静态对象间的初始化依赖关系?因为C++支持单独编译(即,分开编译源模块,然后将多个目标文件链接起来,形成可执行程序),依赖现有的链接器,不会程序数据打交道,所以c++编译器几乎不可能知道整个程序的一切情况。为什么C++不让程序员从一些繁杂事务如内存管理和低级指针操作中解脱出来?因为一些程序员需要这些处理能力,一个真正的程序员的需要自关重要。