C++ 头文件系列(array)

时间:2021-05-12 16:17:18

注意,该头文件仅在C++11中标准才开始出现。

简介

与语言内置的数组一样, array类模版支持几乎所有内置数组包含的特性:

  • 顺序的(sequence)
  • 内存连续的(contiguous storage)
  • 固定大小的(fixed-size)

那既然与内置数组一样,为什么还要定义这样一个模版呢?C++委员会是想造*吗(-_-)?当然不是!

为什么需要array?

array模版类实际上是内置数组的聚合,外加一层封装。正是由于这层接口,才使得数组能与STL接轨,真正成为一个container。

Container接口

  • array::size
  • array::back
  • array::front
  • array::empty
  • array::fill
  • ...

简单通用的array接口,让数组使用起来更加得心应手。 例如size()成员函数返回数组的大小,在内置数组中则只能查看数组定义或者借助外部函数。

Interator模式接口(姑且让我这么叫吧)

  • array::begin
  • array::end
  • array::cbegin
  • array::cend
  • array::rbegin
  • array::rend
  • array::crbegin
  • array::crend

解决语法不一致性

Iterator作为C++中沟通Algorithm与Container的桥梁,起着不可或缺的作用。然而内置类型不支持迭代器的概念,虽然对于算法来说有合适的重载能够解决这个问题,但在语法上存在明显的差别(vec为vector,arr为int数组):

for_each(vec.begin(), vec.end(), [](int i)
{
// something
}); for_each(arr, arr + 3, [](int i)
{
// something
});

其实不需要变量定义声明就可以猜到,vec是container而arr不是。 这也许会阻碍代码后期的重构与修改。使用array类模版的话就不会存在这种问题,统一的语法,多棒!

Bound Check

我们都知道,C++沿袭了C语言的设计,在access数组元素时不对数组变量进行边界检查。array解决了这个问题,在元素读写越界时会抛出异常。

差异性

与内置数组的差异

array与内置数组的一个明显的差异就是----允许空数组的定义

至于为什么要允许这样一个特性,也许是:

  1. 让空数组保持遍历一致性
  2. 让array容器隐式地支持清空(毕竟作为一个fixed-size container,它没有对应的clear操作)。

然而,空数组在语义上是不允许解引用的,因此在使用时规避那些需要解引用的操作。

与其他容器的差异

该方面的差异主要体现在swap成员函数上。就像我们知道的,其他容器调用swap函数时,为了保证高效,该函数实际上执行的操作相当于交换指向实际容器内容的指针。但是array的swap函数却是进行逐成员交换,这也许是为了保持与内置数组相同的性质----数组地址不可变

出于上述语义上的差别,array与其他容器的迭代器,在swap后会有不同的结果(深色方框代表容器对象, 方框内文字代表容器内容; 箭头代表迭代器变量, 都指向头元素):

swap前:

C++ 头文件系列(array)

swap后:

C++ 头文件系列(array)

从图上看更为直接:

  • array模版类对象只是交换了内容而已,两个迭代器的值都未变。
  • 其他容器对象不仅交换了内容,连迭代器的关联性都改变了(实际上,迭代器的值(即关联的内容)也没变,但关联的对象变了)。

Tuple接口

array还提供了一套独特的tuple接口,使其能够像使用元组那样使用。 当然,这种可能性的前提是它本身就非常像元组。

这种特性是通过重载tuple接口的get函数,并且特化tuple_element和tuple_size类模版实现的。