《Hello C++》4.8 数组的替代品

ABSTRACT

  • 零基础读者可以按照章节顺序学习;
  • 已有基础的读者可以按主题查阅,并利用章节总结查漏补缺;
  • 示例代码应当亲手运行、修改和重新实现;
  • 标有标准版本或“选读”的内容,可以根据当前阶段灵活安排;
  • 忘记语法时不必硬背,可以使用第 17 章附录快速查询。

前面我们已经学习了数组,也知道了它可以用来存放一组相同类型的数据。数组凭借自身直接、速度快的特性,一直是 C++ 中非常基础、也非常重要的数据组织方式。不过,普通数组也有一些使用起来不方便的地方,比如大小一旦确定就不容易改变、数组长度信息不会自动跟着对象保存下来、传递给函数后还容易退化成指针。正因为这些原因,C++ 标准库里提供了两种很常用的数组替代品:std::vectorstd::array

它们并不是来“取代”数组的,而是提供了更适合某些场景的选择。你可以把它们理解成不同风格的工具,普通数组像一排固定长度的盒子,vector 像一个可以自动伸缩的收纳箱,而 array 则像一个大小固定、但更规整的盒子。

4.8.1 std::vector

TIP

std::vector定义于头文件 <vector>

std::vector 是 C++ 标准库中的动态数组容器[1]。它最常见的特点,就是大小可以在运行时自动增长或缩小。

程序代码4-8-1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <vector>

using namespace std;

int main()
{
vector<int> numbers;

numbers.push_back(10);
numbers.push_back(20);
numbers.push_back(30);

for (int value : numbers)
{
cout << value << endl;
}

return 0;
}

程序代码4-8-1 先创建了一个空的 vector<int>,然后用 push_back() 依次往里面添加元素。你不用提前写死它的大小,只要需要就可以继续添加。vector 最大的优点之一就是 灵活。当你事先不知道要存多少个元素时,vector 往往比普通数组更合适。

vector 还有一个非常实用的特点,那就是它会自己记住当前有多少元素。你可以用 size() 来查看元素个数:

1
cout << numbers.size() << endl;

因为普通数组的长度需要开发者手动计算,其本身并不会自动告诉你它有多长,但 vector 帮助开发人员省去了计算过程,直接调用“工具”便可获得结果,这比普通数组要方便得多。

4.8.2 std::array

TIP

std::array定义于头文件 <array>

std::array 是 C++11 引入的一个封装了固定大小数组的容器[2]。它和普通数组很像,因为它的大小在创建时就确定了,之后不能再改变。但它和普通数组又不完全一样,因为它是一个标准库对象,功能更完整,也更安全些。

程序代码4-8-2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <array>

using namespace std;

int main()
{
array<int, 5> values = {1, 2, 3, 4, 5};

for (int value : values)
{
cout << value << endl;
}

return 0;
}

程序代码4-8-2 中,array<int, 5> 表示一个能够存放 5 个 int 的固定大小数组。和普通数组相比,array 的一个明显优点是,它是对象,所以你可以直接使用它的一些成员函数,比如 size()at()begin()swap() 等。

1
cout << values.size() << endl;

如果你需要一个“大小固定,但又希望它更像标准库对象”的数组,std::array 就很合适。

4.8.3 普通数组、vectorarray 的区别

这三者都能存放同类型的数据,但它们的特点并不一样。普通数组的特点是简单、直接、开销小。它是 C++ 里最基础的数据结构之一,速度快,也很贴近底层。vector 的特点是灵活。它能在运行时自动调整大小,还能方便地添加和删除元素,特别适合元素个数不确定的情况。array 的特点是固定大小、结构清晰。它和普通数组一样适合保存固定数量的数据,但又比普通数组更规整,因为它是一个完整的对象。

如果把它们放在一起比较,可以简单地记成这样:

  • 普通数组:最基础,最直接,但大小固定,功能较少;
  • vector: 最灵活,适合动态增长的数据;
  • array: 大小固定,但更现代、更安全。

4.8.4 它们在使用上的差别

普通数组的声明很直接:

1
int a[5] = {1, 2, 3, 4, 5};

vector 的写法更像一个容器:

1
vector<int> b = {1, 2, 3, 4, 5};

array 的写法则需要在类型里写出大小:

1
array<int, 5> c = {1, 2, 3, 4, 5};

它们都可以通过下标访问元素:

1
2
3
cout << a[0] << endl;
cout << b[0] << endl;
cout << c[0] << endl;

TIP

截止目前,我们已经简单的了解了成员函数的存在,有读者可能会好奇,我在使用过程中怎么能知道 vectorarray 分别有什么成员函数,以及他们都有什么用呢?没关系,先不用着急,STL 中有很多工具,他们分别都有自己的成员函数用来实现不同的功能。事实上很多开发者都不会全部记住这些成员函数,一来是太多,无法全部记忆;二来是更新频繁,每次标准更新都会添加新的或移除一些不安全的成员。我们只需要在使用过程中,学会查询即可,本书配套网站后续会添加标准库查询功能。

注意

vectorarray 是对象,所以它们除了下标访问之外,还可以更自然地与标准库函数和算法配合。而普通数组则更接近底层,很多时候需要你自己额外小心边界和大小问题。

4.8.5 什么时候用哪个

如果你已经知道数据个数固定,而且不会改变,那么 array 或普通数组都可以。array 更现代一些,也更方便和标准库配合。如果你不知道要放多少数据,或者数据会在运行过程中不断增加,那通常就该考虑 vector。如果你正在做很底层、很接近 C 风格的代码,或者需要和旧代码兼容,普通数组仍然有它的价值。

换句话说,选择哪一种,不是看谁“更高级”,而是看谁更适合当前问题


  1. std::vector,是 C++ 标准库中的动态数组容器。 ↩︎

  2. std::array,是 C++11 引入的固定大小数组容器。 ↩︎