C++ STL array 完全指南

3 阅读10分钟

C++ STL 容器详解:std::array 完全指南

固定大小数组的现代 C++ 封装,兼顾性能与安全

一、概述

std::array 是 C++11 标准库中引入的容器,它封装了固定大小的原生数组,提供了 STL 容器的通用接口(如迭代器、大小查询、边界检查等),同时保持了原生数组的零开销特性。

头文件: <array> 
名字空间:std

核心特点:

  • 数据结构: 数组,空间连续,大小固定。
  • 固定大小:对象定义时大小确定,跟普通的数组一样性质,不可动态增长或缩减。
  • 连续存储:元素在内存中连续排列,兼容 C 风格 API(通过 data()获取首地址)。
  • 零开销:性能与原生数组完全相同,不引入额外运行时负担。即数据个数多于数组元素数量,越界。

二、类模板参数

类模板声明:

template <class T, std::size_t N >
struct array;

T: 元素类型
N: 元素个数(编译期常量,类型为 std::size_t

std::array 的内部实现通常类似于:

template<typename T, size_t N>
struct array {
    T _M_elems[N];   // 原生数组
    // ... 成员函数
};

由于是聚合类型,没有虚函数、没有私有成员,因此对象大小就是 N * sizeof(T)


三、初始化方法

3.1 默认初始化

std::array<int, 5> arr1;       
std::array<int, 5> arr2 = {};     

arr1:默认构造函数,未指定初始值,元素值不确定(局部变量)
arr2:全部元素零初始化:0 0 0 0 0


3.2 聚合初始化(推荐)

聚合初始化即大括号{ }形式初始化,保持原生数组的特点。 int a[5] = {1,2,3,4,5};

方式1:等号 + 花括号,初始化部分元素,剩余元素默认初始化0

std::array<int, 5> a1 = {1, 2, 3};

a1:1 2 3 0 0
初始化部分元素,剩余元素默认初始化0。


方式2:直接花括号(C++14起单层即可) 

std::array<int, 3> a2 {1, 2, 3};         // 1 2 3
std::array<int, 3> a3 {{1, 2, 3}};       // 1 2 3

C++11使用双层大括号,C++14后单层双层均可,一般不用双层大括号,麻烦。


方式3:二维数组:3 x 2。3行2列。

std::array<std::array<int, 2>, 3> a4 = {1,2,3,4,5,6}; 

可以理解成如下:

1 2
3 4
5 6


3.3 拷贝/移动构造

函数原型:

  array() noexcept;               //默认构造
  array(const array& other);      //拷贝构造
  array(array&& other) noexcept;  //移动构造

示例:

std::array<int, 4> a1 = {1,2,3,4};      // 聚合构造
std::array<int, 4> a2 = src;            // 拷贝构造
std::array<int, 4> a3 = std::move(src); // 移动构造(元素逐移动)

3.4 重新赋值:赋值运算符:=

std::array<int, 6> a = { 1,2,3,4,5,6 };
a = { 7,8,9,6,5,3 };                     //重新赋值,不能超过6个元素

注意:不能超过元素个数。a是6个元素的数组,最大赋值不能超过6个。


四、元素获取函数

4.1 元素引用:at( )

带边界检查的访问,越界抛出 std::out_of_range

函数原型:

T& at(size_type pos);
const T& at(size_type pos) const;

示例:

int main(void)
{
	std::array<int, 6> a1 = { 1,2,3,4,5,6 };
	cout << a1.at(2) << endl;  // 3
	a1.at(2) = 10;             // 1,2,10,4,5,6

	// a1.at(7) = 56;             // 越界,严重错误

	std::array<std::array<int, 2>, 3> a2{ 1, 2, 3, 4, 5, 6};
	cout << a2.at(2).at(0) << endl;  // 5
	a2.at(2).at(0) = 10;             // 1, 2, 3, 4, 10, 6

	return 0;
}

4.2 元素引用:下标运算符:[ ]

函数原型:

T& operator[](size_type pos);
const T& operator[](size_type pos) const;

示例:

int main(void)
{
	std::array<int, 6> a1 = { 1,2,3,4,5,6 };
	cout << a1[2] << endl;  // 3
	a1[2] = 10;             // 1,2,10,4,5,6

	// a1[7] = 56;             // 越界,严重错误

	std::array<std::array<int, 2>, 3> a2{ 1, 2, 3, 4, 5, 6};
	cout << a2[2][0] << endl;  // 5
	a2[2][0] = 10;             // 1, 2, 3, 4, 10, 6

	return 0;
}

4.3 首元素引用:front( )

函数原型:

T& front();
const T& front() const;

示例:

std::array<int, 6> a1 = { 1,2,3,4,5,6 };
cout << a1.front() << endl;  // 1
a1.front() = 10;             // 10,2,3,4,5,6
cout << a1.front() << endl;  // 10

4.4 尾元素引用:back( )

函数原型:

T& back();
const T& back() const;

示例:

std::array<int, 6> a1 = { 1,2,3,4,5,6 };
cout << a1.back() << endl;  // 6
a1.back() = 10;             // 1,2,3,4,5,10
cout << a1.back() << endl;  // 10

4.5 数组首地址:data( )

返回指向底层数组的首地址。

函数原型:

T* data() noexcept;
const T* data() const noexcept;

示例:

std::array<int, 6> a1 = { 1,2,3,4,5,6 };
int* p = a1.data();
for (int i = 0; i < 5; i++)
	cout << p[i] << ' ';    // 1 2 3 4 5

五、迭代器

iterator                               : 正向迭代器
const_iterator                     : 正向常量迭代器
reverse_iterator                  :反向迭代器   
const_reverse_iterator        :反向常量迭代器

5.1 array迭代器运算符:

1. 解引用运算符:*  ,  ->
运算符说明
*it返回迭代器当前指向元素的引用
->元素是(类)类型,访问元素的成员
2. 递增/递减运算符: ++ , --
运算符说明
++it前置递增,迭代器指向下一个元素,返回新迭代器的引用
it++后置递增,迭代器指向下一个元素,返回原迭代器的副本
--it前置递减,迭代器指向前一个元素,返回新迭代器的引用
it--后置递减,迭代器指向前一个元素,返回原迭代器的副本
3. 算术运算符: + , - , += , -=
运算符说明
it + n返回迭代器向后移动 n 个位置的新迭代器
n + it同上
it - n返回迭代器向前移动 n 个位置的新迭代器
it += n迭代器向后移动 n 个位置,返回自身引用
it -= n迭代器向前移动 n 个位置,返回自身引用
it1 - it2返回两个迭代器之间的距离(ptrdiff_t),可为负数
4. 下标运算符 : [ ]
运算符说明
it[n]等价于 *(it + n),返回偏移 n 个位置的元素的引用
5. 比较运算符: == , != , > , < , >= , <=
运算符说明
== 、!=判断两个迭代器是否指向同一位置,同位置返回1,不同返回0
<  、 >比较迭代器的前后关系(位置),前 < 后
<=  、 >=小于等于 / 大于等于,前 < 后

5.2 正向迭代器:begin() / end()

函数原型:

iterator begin();
iterator end();

可读可写。

示例:

int main(void)
{
	std::array<int, 6> a = { 1,2,3,4,5,6 };
	std::array<int, 6>::iterator itb = a.begin();
	auto ite = a.end();                           // auto更方便

	while (itb != ite) {
		std::cout << *itb++ << std::endl;
	}
	
	return 0;
}

5.3 常量正向迭代器:cbegin() / cend()

只读。

函数原型:

const_iterator begin() const;   常函数
const_iterator cbegin() const;
const_iterator end() const;     常函数
const_iterator cend() const;

示例:

int main(void)
{
	std::array<int, 6> a = { 1,2,3,4,5,6 };
	std::array<int, 6>::const_iterator itb = a.cbegin();
	auto ite = a.cend();                           // auto更方便

	while (itb != ite) {
		std::cout << *itb++ << std::endl;
	}
	
	return 0;
}

5.4 反向迭代器:rbegin() / rend()

反向,可读可写。

函数原型:

reverse_iterator rbegin();
reverse_iterator rend();

示例:

int main(void)
{
	std::array<int, 6> a = { 1,2,3,4,5,6 };
	std::array<int, 6>::reverse_iterator itb = a.rbegin();
	auto ite = a.rend();                           // auto更方便

	while (itb != ite) {
		std::cout << *itb++ << ' ';  //  6 5 4 3 2 1
	}
	
	return 0;
}

5.5 常量反向迭代器:crbegin() / crend()

反向,只读。

函数原型:

const_reverse_iterator rbegin() const;    常对象
const_reverse_iterator crbegin() const;
const_reverse_iterator rend() const;      常对象 
const_reverse_iterator crend() const; 

示例:

int main(void)
{
	std::array<int, 6> a = { 1,2,3,4,5,6 };
	std::array<int, 6>::const_reverse_iterator itb = a.rbegin();
	auto ite = a.rend();                           // auto更方便

	while (itb != ite) {
		std::cout << *itb++ << ' ';  //  6 5 4 3 2 1
	}
	
	return 0;
}

六、其他成员函数

6.1 元素个数:size( )

函数原型:

bool empty() const;

示例:

int main(void)
{
	std::array<int, 6> a = { 1,2,3,4,5,6 };
	cout << a.size() << endl;            // 6
	cout << a.max_size() << endl;        // 6
	
	return 0;
}

6.2 素数个数:max_size( )

返回 N(与 size() 相同)。

函数原型:

size_type max_size() const;

示例:

int main(void)
{
	std::array<int, 6> a = { 1,2,3,4,5,6 };
	cout << a.size() << endl;            // 6
	cout << a.max_size() << endl;        // 6
	
	return 0;
}

6.3 判断空:empty( )

函数原型:

空返回1,非空返回0
bool empty() const;

示例:

int main(void)
{
	std::array<int, 6> a = { 1,2,3,4,5,6 };
	cout << a.empty() << endl;           // 0  非空
	std::array<int, 0> d;
	cout << d.empty() << endl;           // 1  空
	
	return 0;
}

6.4 所有元素赋值:fill( )

函数原型:

将数组所有元素设置为value
void fill( const T& value );

示例:

int main(void)
{
	std::array<int, 6> a = { 1,2,3,4,5,6 };
	a.fill(8);            // 8 8 8 8 8 8 
}

6.5 交换对象元素:swap( )

交换两个数组的内容(需同类型同大小)。元素个数不同语法报错。

函数原型:

void swap( array& other );

示例:

std::array<int, 6> a = { 1,2,3,4,5,6 };
std::array<int, 6> c = { 8,8,8,8,8,8 };
a.swap(c);    // 元素交换

七、非成员函数

7.1 比较运算符:==, !=, <, <=, >, >=

函数原型:

template< class T, std::size_t N >
bool operator==( const std::array<T, N>& lhs,const std::array<T, N>& rhs );

template< class T, std::size_t N >
bool operator!=( const std::array<T, N>& lhs,const std::array<T, N>& rhs );

template< class T, std::size_t N >
bool operator< ( const std::array<T, N>& lhs,const std::array<T, N>& rhs );

template< class T, std::size_t N >
bool operator<=( const std::array<T, N>& lhs,const std::array<T, N>& rhs );

template< class T, std::size_t N >
bool operator> ( const std::array<T, N>& lhs,const std::array<T, N>& rhs );

template< class T, std::size_t N >
bool operator>=( const std::array<T, N>& lhs,const std::array<T, N>& rhs );

示例:

std::array<int,3> a = {1,2,3};
std::array<int,3> b = {1,2,4};
std::cout << (a < b) << std::endl;   // 1 (true)

std::array 采用字典序比较。

示例:

结构体类型元素需重载结构体比较的运算符。如下:

struct Node
{
	int a;
	int d;
};

bool operator>(const Node& l, const Node& r)
{
	return (l.a > r.a);
}
bool operator<(const Node& l, const Node& r)
{
	return (l.a < r.a);
}
int main(void)
{
	std::array<Node, 3> a = { 1,2, 3,4, 5,6};
	std::array<Node, 3> c = { 8,8, 8,8, 8,8 };
	cout << (a < c) << endl;
	
	return 0;
}

7.2 编译期元素引用:get( )

延伸阅读

C++17 添加了结构化绑定,可与 std::array 配合使用。

C++20 的 std::to_array 进一步简化了创建过程。

拥抱现代 C++,从使用 std::array 开始!

函数原型:

namespace std {
    // 获取普通左值引用
    template<size_t I, class T, size_t N>
    constexpr T& get(array<T,N>& arr) noexcept;

    // 获取 const 左值引用
    template<size_t I, class T, size_t N>
    constexpr const T& get(const array<T,N>& arr) noexcept;

    // 获取右值引用(移动语义)
    template<size_t I, class T, size_t N>
    constexpr T&& get(array<T,N>&& arr) noexcept;

    // 获取 const 右值引用(极少使用)
    template<size_t I, class T, size_t N>
    constexpr const T&& get(const array<T,N>&& arr) noexcept;
}

示例:

int main(void)
{
	std::array<int, 6> a = { 1,2, 3,4, 5,6};
	get<1>(a) = 10;     // 赋值
	int& d = get<1>(a);  

	return 0;
}

关键特性:

特性说明
编译期索引索引 必须是编译期常量(如字面量、constexpr 变量),不能是运行时变量
边界检查索引越界(I >= N)会导致编译错误(而非运行时异常)
返回值类型根据 arr 的值类别返回相应的引用(左值、const 左值、右值引用)
与 []/at()的区别operator[] 使用运行时索引,无边界检查,越界运行异常at(i)运行时索引,需要边界检查std::get 使用编译期索引,越界直接编译失败
noexcept始终不抛出异常

5.3 std::to_array (C++20)

从内建数组或初始化列表创建 std::array,自动推导大小。

函数原型:

template< class T, std::size_t N >
constexpr std::array<std::remove_cv_t<T>, N> to_array( T (&a)[N] );

template< class T, std::size_t N >
constexpr std::array<std::remove_cv_t<T>, N> to_array( T (&&a)[N] );

示例:

int main() {
    // 从 C 风格数组创建
    int carr[] = {1, 2, 3, 4};
    auto arr1 = std::to_array(carr);        // std::array<int, 4>

    // 从字面量创建(字符串)
    auto arr2 = std::to_array("hello");     // std::array<char, 6> 包含 '\0'

    // 从初始化列表创建(需要显式指定类型)
    auto arr3 = std::to_array<int>({10, 20, 30});  // std::array<int, 3>
}

八、总结

8.1. 与原生数组的对比

特性std::array原生数组 T[N]
大小信息有 size() 成员,编译期常量无,需额外传递
边界检查at() 提供运行时检查
赋值/拷贝支持 = 赋值,可拷贝不支持直接赋值
作为函数参数按值传递(实际是拷贝)或引用传递退化为指针
与 STL 算法兼容完全兼容(提供迭代器)需使用 std::begin/end
性能零开销,等同原生基准
内存位置栈上(与原生相同)栈上

8.2. 性能与边界安全最佳实践

  • 默认使用 at() 用于调试:在开发阶段启用边界检查捕获越界错误;发布版本可回退到 operator[] 以获得最高性能。
  • 使用 fill() 统一赋值:比手动循环更简洁,且可能被编译器优化。
  • 利用 std::array 作缓冲区:结合 data() 调用 C 库函数,例如 write(fd,  arr.data(), arr.size());
  • 避免大数组按值传递:如果 N 很大,拷贝开销不可忽略,应使用 const std::array<T,N>&

8.3 特点总结

适用场景说明
✅ 需要固定大小的数组编译期已知元素个数
✅ 需要与 STL 算法无缝协作迭代器接口完备
✅ 需要边界检查的可选支持at() 成员函数
✅ 追求零开销抽象性能与原生数组完全相同
❌ 需要动态扩容请使用 std::vector
❌ 大小在运行时才能确定请使用 std::vector 或动态分配

std::array 填补了原生数组和 std::vector 之间的空白,是现代 C++ 中固定大小数组的首选类型。尽量用它替代原生数组,以获得类型安全、大小信息自包含以及丰富的成员函数支持。