泛型编程(www.cppentry.com)与C++(www.cppentry.com)标准库
C++(www.cppentry.com)最强大的特性之一就是对泛型编程(www.cppentry.com)的支持。C++(www.cppentry.com)标准库的高度灵活性就是明证,尤其是标准库中的容器、迭代器以及算法部分(最初也称为STL)。
与我的另一本书More Exceptional C++(www.cppentry.com) [Sutter02]一样,本书的开头几条也是介绍STL中一些我们平常熟悉的部件,如vector和string,另外也介绍了一些不那么常见的设施。例如,在使用最基本的容器vector时如何避免常见的陷阱?如何在C++(www.cppentry.com)中进行常见的C风格字符串操纵?我们能够从STL中学到哪些库设计经验(不管是好的、坏的,还是极其糟糕的)?
在考察了STL中的模板设施之后,接着讨论关于C++(www.cppentry.com)中的模板和泛型编程(www.cppentry.com)的一些更一般性的问题。例如,如何让我们的模板代码避免不必要地(且相当不经意地)损失泛型性。为什么说特化函数模板实际上是个糟糕的主意,而我们又应当怎么替换它?在模板的世界中,我们如何才能正确且可移植地完成像授予友元关系这样看似简单的操作?此外还有围绕着export这个有趣的关键字发生的种种故事。
随着我们逐步深入与C++(www.cppentry.com)标准库及泛型编程(www.cppentry.com)相关的主题,就会看到关于上述(以及其他)问题的讨论。
第1条 vector的使用(1) 难度系数:4
几乎每个人都会使用std::vector,这是个好现象。不过遗憾的是,许多人都误解了它的语义,结果无意间以奇怪和危险的方式使用它。本条款中阐述的哪些问题会出现在你目前的程序中呢?
初级问题
1. 下面的代码中,注释A跟注释B所示的两行代码有何区别?
- void f(vector<int>& v) {
- v[0]; // A
- v.at(0); // B
- }
专家级问题
2. 考虑如下的代码:
- vector<int> v;
-
- v.reserve(2);
- assert(v.capacity() == 2);
- v[0] = 1;
- v[1] = 2;
- for(vector<int>::iterator i = v.begin(); i < v.end(); i++) {
- cout << *i << endl;
- }
-
- cout << v[0];
- v.reserve(100);
- assert(v.capacity() == 100);
- cout << v[0];
-
- v[2] = 3;
- v[3] = 4;
- // ……
- v[99] = 100;
- for(vector<int>::iterator i = v.begin(); i < v.end(); i++) {
- cout << *i << endl;
- }
请从代码的风格和正确性方面对这段代码做出评价。
解决方案
访问vector的元素
1. 下面的代码中,注释A跟注释B所示的两行代码有何区别?
- // 示例1-1: [] vs. at
- //
- void f(vector<int>& v) {
- v[0]; // A
- v.at(0); // B
- }
在示例1-1中,如果v非空,A行跟B行就没有任何区别;如果v为空,B行一定会抛出一个std::out_of_range异常,至于A行的行为,标准未加任何说明。
有两种途径可以访问vector内的元素。其一,使用vector::at。该成员函数会进行下标越界检查,确保当前vector中的确包含了需要的元素。试图在一个目前只包含10个元素的vector中访问第100个元素是毫无意义的,这样做会导致抛出一个std::out_of_range异常。
其二,我们也可以使用vector::operator[],C++(www.cppentry.com)98标准说vector::operator可以、但不一定要进行下标越界检查。实际上,标准对operator[]是否需要进行下标越界检查只字未提,不过标准同样也没有说它是否应该带有异常规格声明。因此,标准库实现方可以自由选择是否为operator[]加上下标越界检查功能。如果使用operator[]访问一个不在vector中的元素,你可就得自己承担后果了,标准对这种情况下会发生什么事情没有做任何担保(尽管你使用的标准库实现的文档可能做了某些保证)--你的程序可能会立即崩溃,对operator[]的调用也许会引发一个异常,甚至也可能看似无恙,不过会偶尔或神秘地出问题。
既然下标越界检查帮助我们避免了许多常见问题,那为什么标准不要求operator[]实施下标越界检查呢?简短的答案是效率。总是强制下标越界检查会增加所有程序的性能开销(虽然不大),即使有些程序根本不会越界访问。有一句名言反映了C++(www.cppentry.com)的这一精神:一般说来,不应该为不使用的东西付出代价(或开销)。所以,标准并不强制operator[]进行越界检查。况且我们还有另一个理由要求operator[]具有高效性:设计vector是用来替代内置数组的,因此其效率应该与内置数组一样,内置数组在下标索引时是不进行越界检查的。如果你需要下标越界检查,可以使用at。
调整vector的大小
现在看示例1-2,该示例对vector进行了简单操作。