46-比较数组、vector对象和array对象
4.10.3 比较数组、vector对象和array对象
要了解数组、vector对象和array对象的相似和不同之处,最简单的方式可能是看一个使用它们的简单示例,如程序清单4.24所示。
程序清单4.24 choices.cpp
// choices.cpp -- array variations
#include <iostream>
#include <vector> // STL C++98
#include <array> // C++11
int main()
{
using namespace std;
// C, original C++
double a1[4] = {1.2, 2.4, 3.6, 4.8};
// C++98 STL
vector<double> a2(4); // create vector with 4 elements
// no simple way to initialize in C98
a2[0] = 1.0/3.0;
a2[1] = 1.0/5.0;
a2[2] = 1.0/7.0;
a2[3] = 1.0/9.0;
// C++11 -- create and initialize array object
array<double, 4> a3 = {3.14, 2.72, 1.62, 1.41};
array<double, 4> a4;
a4 = a3; // valid for array objects of same size
// use array notation
cout << "a1[2]: " << a1[2] << " at " << &a1[2] << endl;
cout << "a2[2]: " << a2[2] << " at " << &a2[2] << endl;
cout << "a3[2]: " << a3[2] << " at " << &a3[2] << endl;
cout << "a4[2]: " << a4[2] << " at " << &a4[2] << endl;
// misdeed
a1[-2] = 20.2;
cout << "a1[-2]: " << a1[-2] <<" at " << &a1[-2] << endl;
cout << "a3[2]: " << a3[2] << " at " << &a3[2] << endl;
cout << "a4[2]: " << a4[2] << " at " << &a4[2] << endl;
return 0;
}
下面是该程序的输出示例:
a1[2]: 3.6 at 0x28cce8
a2[2]: 0.142857 at 0xca0328
a3[2]: 1.62 at 0x28ccc8
a4[2]: 1.62 at 0x28cca8
a1[-2]: 20.2 at 0x28ccc8
a3[2]: 20.2 at 0x28ccc8
a4[2]: 1.62 at 0x28cca8
程序说明
首先,注意到无论是数组、vector对象还是array对象,都可使用标准数组表示法来访问各个元素。其次,从地址可知,array对象和数组存储在相同的内存区域(即栈)中,而vector对象存储在另一个区域(自由存储区或堆)中。第三,注意到可以将一个array对象赋给另一个array对象;而对于数组,必须逐元素复制数据。
接下来,下面一行代码需要特别注意:
a1[-2] = 20.2;
索引-2是什么意思呢?本章前面说过,这将被转换为如下代码:
*(a1-2) = 20.2;
其含义如下:找到a1指向的地方,向前移两个double元素,并将20.2存储到目的地。也就是说,将信息存储到数组的外面。与C语言一样,C++也不检查这种超界错误。在这个示例中,这个位置位于array对象a3中。其他编译器可能将20.2放在a4中,甚至做出更糟糕的选择。这表明数组的行为是不安全的。
vector和array对象能够禁止这种行为吗?如果您让它们禁止,它们就能禁止。也就是说,您仍可编写不安全的代码,如下所示:
a2[-2] = .5; // still allowed
a3[200] = 1.4;
然而,您还有其他选择。一种选择是使用成员函数at()。就像可以使用cin对象的成员函数getline()一样,您也可以使用vector和array对象的成员函数at():
a2.at(1) = 2.3; // assign 2.3 to a2[1]
中括号表示法和成员函数at()的差别在于,使用at()时,将在运行期间捕获非法索引,而程序默认将中断。这种额外检查的代价是运行时间更长,这就是C++ 允许您使用任何一种表示法的原因所在。另外,这些类还让您能够降低意外超界错误的概率。例如,它们包含成员函数begin()和end(),让您能够确定边界,以免无意间超界,这将在第16章讨论。