C++ STL array容器访问元素的几种方式

当 array 容器创建完成之后,最常做的操作就是获取其中的元素,甚至有时还会通过循环结构获取多个元素。本节就对获取容器中元素的方法做个汇总。

访问array容器中单个元素

首先,可以通过容器名[]的方式直接访问和使用容器中的元素,这和 C++ 标准数组访问元素的方式相同,例如:
values[4] = values[3] + 2.O*values[1];
此行代码中,第 5 个元素的值被赋值为右边表达式的值。需要注意的是,使用如上这样方式,由于没有做任何边界检查,所以即便使用越界的索引值去访问或存储元素,也不会被检测到。

为了能够有效地避免越界访问的情况,可以使用 array 容器提供的 at() 成员函数,例如 :
values.at (4) = values.at(3) + 2.O*values.at(1);
这行代码和前一行语句实现的功能相同,其次当传给 at() 的索引是一个越界值时,程序会抛出 std::out_of_range 异常。因此当需要访问容器中某个指定元素时,建议大家使用 at(),除非确定索引没有越界。

读者可能有这样一个疑问,即为什么 array 容器在重载 [] 运算符时,没有实现边界检查的功能呢?答案很简单,因为性能。如果每次访问元素,都去检查索引值,无疑会产生很多开销。当不存在越界访问的可能时,就能避免这种开销。

除此之外,array 容器还提供了 get<n> 模板函数,它是一个辅助函数,能够获取到容器的第 n 个元素。需要注意的是,该模板函数中,参数的实参必须是一个在编译时可以确定的常量表达式,所以它不能是一个循环变量。也就是说,它只能访问模板参数指定的元素,编译器在编译时会对它进行检查。

下面代码展示了如何使用 get<n> 模板函数:
#include <iostream>
#include <array>
#include <string>
using namespace std;
int main()
{
    array<string, 5> words{ "one","two","three","four","five" };
    cout << get<3>(words) << endl; // Output words[3]
    //cout << get<6>(words) << std::endl; //越界,会发生编译错误
    return 0;
}
运行结果为:

four

另外,array 容器提供了 data() 成员函数,通过调用该函数可以得到指向容器首个元素的指针。通过该指针,我们可以获得容器中的各个元素,例如:
#include <iostream>
#include <array>
using namespace std;
int main()
{
    array<int, 5> words{1,2,3,4,5};
    cout << *( words.data()+1);
    return 0;
}
运行结果为:

2

访问array容器中多个元素

我们知道,array 容器提供的 size() 函数能够返回容器中元素的个数(函数返回值为 size_t 类型),所以能够像下面这样去逐个提取容器中的元素,并计算它们的和:
double total = 0;
for(size_t i = 0 ; i < values.size() ; ++i)
{
    total += values[i];
}
size() 函数的存在,为 array 容器提供了标准数组所没有的优势,即能够知道它包含多少元素。

并且,接受数组容器作为参数的函数,只需要通过调用容器的成员函数 size(),就能得到元素的个数。除此之外,通过调用 array 容器的 empty() 成员函数,即可知道容器中有没有元素(如果容器中没有元素,此函数返回 true),如下所示:
if(values.empty())
    std::cout << "The container has no elements.\n";
else
    std::cout << "The container has "<< values.size()<<"elements.\n";
然而,很少会创建空的 array 容器,因为当生成一个 array 容器时,它的元素个数就固定了,而且无法改变,所以生成空 array 容器的唯一方法是将模板的第二个参数指定为 0,但这种情况基本不可能发生。

array 容器之所以提供 empty() 成员函数的原因,对于其他元素可变或者元素可删除的容器(例如 vector、deque 等)来说,它们使用 empty() 时的机制是一样的,因此为它们提供了一个一致性的操作。

除了借助 size() 外,对于任何可以使用迭代器的容器,都可以使用基于范围的循环,因此能够更加简便地计算容器中所有元素的和,比如:
double total = 0;
for(auto&& value : values)
    total += value;

下面是一个示例,展示了本节关于如何获取 array 容器中元素所讲到的知识:
#include <iostream>
#include <iomanip> 
#include <array>
using namespace std;
int main()
{
    array<int, 5> values1;
    array<int, 5> values2;
    //初始化 values1 为 {0,1,2,3,4}
    for (size_t i = 0; i < values1.size(); ++i)
    {
        values1.at(i) = i;
    }

    cout << "values1[0] is : " << values1[0] << endl;
    cout << "values1[1] is : " << values1.at(1) << endl;
    cout << "values1[2] is : " << get<2>(values1) << endl;

    //初始化 values2 为{10,11,12,13,14}
    int initvalue = 10;
    for (auto& value : values2)
    {
        value = initvalue;
        initvalue++;
    }
    cout <<  "Values1 is : ";
    for (auto i = values1.begin(); i < values1.end(); i++) {
        cout << *i << " ";
    }

    cout << endl << "Values2 is : ";
    for (auto i = values2.begin(); i < values2.end(); i++) {
        cout << *i << " ";
    }
    return 0;
}
运行结果为:

values1[0] is : 0
values1[1] is : 1
values1[2] is : 2
Values1 is : 0 1 2 3 4
Values2 is : 10 11 12 13 14