概述
"通于一而万事毕" ——《庄子·天地》
std::array 是C风格数组的替代品,增强版的固定大小的数组。其"道"在于编译时确定的连续内存布局和零开销抽象;其"术"在于丰富的STL接口和类型安全操作。
道法篇
在《C++ STL 容器 -- 道与术 篇(一) vector》中,详细介绍了顺序列表vector的原理以及接口。array 跟vector类似,也是顺序列表,但与vector不同的是,array的大小是固定的,在编译时就决定了大小,不能在运行过程中进行修改,与C的数组类似。
vector 两个重点:
(1)在内存布局上是连续的--> 支持随机访问。
(2)固定大小。
系列特性说明:
基于这两个"道法"原理,决定了其一系列特性:
(1)编译时大小确定
template<typename T, std::size_t N>
class array {
T _M_elems[N]; // 内嵌固定大小数组
// 没有动态内存管理开销
};

上图为一个大小为16 的aray的简单示例。
(2)性能优势
-
访问复杂度:O(1) 随机访问
-
无动态分配:编译期确定大小,无堆分配开销
-
缓存友好:连续内存布局,提高缓存命中率
(3)内存开销分析
#include <iostream>
#include <array>
#include <stdint.h>
struct Test {
uint64_t a, b, c; // 24字节
};
int main() {
std::array<Test, 3> arr;
std::cout << "array大小: " << sizeof(arr) << "字节\n"; // 72字节
std::cout << "元素大小: " << sizeof(Test) << "字节\n"; // 24字节
// 总大小 = 3 × 24 = 72字节,无额外开销
}
/*
输出:
array大小: 72字节
元素大小: 24字节
*/
(4)与vector的内存对比
std::array<int, 100> fixed_arr; // 栈上分配,400字节
std::vector<int> dynamic_vec(100); // 堆上分配,额外管理开销
// array内存布局:
// [栈帧]...[array数据]...[其他栈变量]
// vector内存布局:
// [栈帧]...[vector控制块]...[堆上的数据]
编译时能力
#include <array>
// 编译时计算
constexpr std::array<int, 5> create_array() {
std::array<int, 5> arr{};
for (int i = 0; i < 5; ++i) {
arr[i] = i * i;
}
return arr;
}
constexpr auto squares = create_array();
// 编译期就已经计算完成:{0, 1, 4, 9, 16}
使用 std::array 而不是C数组的主要理由:
-
✅ 安全性:提供边界检查选项
-
✅ 现代化:完整的STL容器接口
-
✅ 便利性:内置有用成员函数,支持赋值操作
-
✅ 类型安全:保持大小信息,不会意外退化为指针
-
✅ 零开销:与C数组相同的性能和内存布局
-
✅ 可维护性:更清晰、更少错误的代码
术法篇
前面通过"道法"篇详细介绍了array的底层原理和内存布局。至此,你应该理解其零开销抽象的本质和性能优势。下面全面呈现std::array的完整API。
📋 完整API
🏗️ 成员类型
| 类型 | 定义 | 说明 |
|---|---|---|
value_type | T | 容器中存储的元素类型 |
size_type | std::size_t | 无符号整数类型,用于表示大小 |
difference_type | std::ptrdiff_t | 有符号整数类型,用于表示距离 |
reference | value_type& | 元素的引用类型 |
const_reference | const value_type& | 元素的常量引用类型 |
pointer | value_type* | 指向元素的指针类型 |
const_pointer | const value_type* | 指向常量元素的指针类型 |
iterator | 随机访问迭代器 | 指向元素的迭代器 |
const_iterator | 常量随机访问迭代器 | 指向常量元素的迭代器 |
reverse_iterator | std::reverse_iterator<iterator> | 反向迭代器 |
const_reverse_iterator | std::reverse_iterator<const_iterator> | 常量反向迭代器 |
🏗️ 构造函数
| 构造函数 | 说明 | 示例 |
|---|---|---|
array() | 默认构造(值初始化) | std::array<int, 3> arr; |
array(const array& other) | 拷贝构造 | std::array<int, 3> arr2(arr1); |
array(array&& other) noexcept | 移动构造 | std::array<int, 3> arr3(std::move(arr1)); |
array(std::initializer_list<T> init) | 初始化列表构造 | std::array<int, 3> arr4{1,2,3}; |
🗑️ 析构函数
| 函数 | 说明 | 示例 |
|---|---|---|
~array() | 销毁所有元素 | 自动调用 |
🔄 赋值操作
| 函数 | 说明 | 示例 | 时间复杂度 |
|---|---|---|---|
array& operator=(const array& other) | 拷贝赋值 | arr2 = arr1; | O(n) |
array& operator=(array&& other) noexcept | 移动赋值 | arr3 = std::move(arr1); | O(n) |
array& operator=(std::initializer_list<T> ilist) | 初始化列表赋值 | arr4 = {4,5,6}; | O(n) |
🔍 元素访问
| 函数 | 说明 | 示例 | 异常安全 |
|---|---|---|---|
reference operator[](size_type pos) | 访问指定元素 | arr[0] = 1; | 无检查 |
const_reference operator[](size_type pos) const | const版本 | int x = arr[0]; | 无检查 |
reference at(size_type pos) | 边界检查访问 | arr.at(0) = 1; | 越界抛出异常 |
const_reference at(size_type pos) const | const版本 | int x = arr.at(0); | 同上 |
reference front() | 访问第一个元素 | arr.front() = 1; | 空数组未定义 |
const_reference front() const | const版本 | int x = arr.front(); | 同上 |
reference back() | 访问最后一个元素 | arr.back() = 1; | 空数组未定义 |
const_reference back() const | const版本 | int x = arr.back(); | 同上 |
T* data() noexcept | 访问底层数组 | int* p = arr.data(); | 无 |
const T* data() const noexcept | const版本 | const int* p = arr.data(); | 无 |
🔄 迭代器
| 函数 | 说明 | 示例 |
|---|---|---|
iterator begin() noexcept | 指向首元素的迭代器 | auto it = arr.begin(); |
const_iterator begin() const noexcept | const版本 | auto it = arr.begin(); |
const_iterator cbegin() const noexcept | 指向首元素的const迭代器 | auto it = arr.cbegin(); |
iterator end() noexcept | 指向尾后位置的迭代器 | auto it = arr.end(); |
const_iterator end() const noexcept | const版本 | auto it = arr.end(); |
const_iterator cend() const noexcept | 指向尾后位置的const迭代器 | auto it = arr.cend(); |
reverse_iterator rbegin() noexcept | 指向反向首元素的迭代器 | auto it = arr.rbegin(); |
const_reverse_iterator rbegin() const noexcept | const版本 | auto it = arr.rbegin(); |
const_reverse_iterator crbegin() const noexcept | 指向反向首元素的const迭代器 | auto it = arr.crbegin(); |
reverse_iterator rend() noexcept | 指向反向尾后位置的迭代器 | auto it = arr.rend(); |
const_reverse_iterator rend() const noexcept | const版本 | auto it = arr.rend(); |
const_reverse_iterator crend() const noexcept | 指向反向尾后位置的const迭代器 | auto it = arr.crend(); |
📦 容量操作
| 函数 | 说明 | 示例 | 时间复杂度 |
|---|---|---|---|
bool empty() const noexcept | 检查是否为空 | if(arr.empty()) | O(1) |
size_type size() const noexcept | 返回元素数量 | size_t s = arr.size(); | O(1) |
size_type max_size() const noexcept | 返回可容纳的最大元素数 | size_t m = arr.max_size(); | O(1) |
🛠️ 修改器
| 函数 | 说明 | 示例 | 时间复杂度 |
|---|---|---|---|
void fill(const T& value) | 用value填充所有元素 | arr.fill(42); | O(n) |
void swap(array& other) noexcept | 交换内容 | arr1.swap(arr2); | O(n) |
🔗 非成员函数
比较操作
| 函数 | 说明 | 示例 |
|---|---|---|
bool operator==(const array& lhs, const array& rhs) | 相等比较 | if(arr1 == arr2) |
bool operator!=(const array& lhs, const array& rhs) | 不等比较 | if(arr1 != arr2) |
bool operator<(const array& lhs, const array& rhs) | 小于比较 | if(arr1 < arr2) |
bool operator<=(const array& lhs, const array& rhs) | 小于等于比较 | if(arr1 <= arr2) |
bool operator>(const array& lhs, const array& rhs) | 大于比较 | if(arr1 > arr2) |
bool operator>=(const array& lhs, const array& rhs) | 大于等于比较 | if(arr1 >= arr2) |
C++20 三路比较
| 函数 | 说明 | 示例 |
|---|---|---|
template<class T, size_t N> auto operator<=>(const array<T,N>& lhs, const array<T,N>& rhs) | 三路比较 | auto cmp = arr1 <=> arr2; |
交换操作
| 函数 | 说明 | 示例 |
|---|---|---|
void swap(array& lhs, array& rhs) noexcept | 特化swap算法 | std::swap(arr1, arr2); |
元函数 (C++17)
| 函数 | 说明 | 示例 |
|---|---|---|
template<class T, size_t N> struct tuple_size<array<T,N>> | 获取array的大小 | auto size = std::tuple_size<arr_type>::value; |
template<size_t I, class T, size_t N> struct tuple_element<I, array<T,N>> | 获取array指定元素的类型 | using type = std::tuple_element<0, arr_type>::type; |
元素访问 (C++17)
| 函数 | 说明 | 示例 |
|---|---|---|
template<size_t I, class T, size_t N> T& get(array<T,N>& a) noexcept | 访问指定位置的元素 | int x = std::get<0>(arr); |
template<size_t I, class T, size_t N> T&& get(array<T,N>&& a) noexcept | 访问指定位置的元素(右值) | int x = std::get<0>(std::move(arr)); |
template<size_t I, class T, size_t N> const T& get(const array<T,N>& a) noexcept | const版本 | int x = std::get<0>(arr); |
擦除操作 (C++20)
| 函数 | 说明 | 示例 |
|---|---|---|
template<class T, size_t N, class U> size_t erase(array<T,N>& c, const U& value) | 从array中擦除所有等于value的元素 | std::erase(arr, 10); |
template<class T, size_t N, class Pred> size_t erase_if(array<T,N>& c, Pred pred) | 从array中擦除所有满足pred的元素 | std::erase_if(arr, [](int x){return x%2==0;}); |
💻 完整代码示例
#include <iostream>
#include <array>
#include <algorithm>
#include <tuple>
int main() {
// 1. 创建和初始化
std::array<int, 5> arr = {1, 2, 3, 4, 5};
std::array<int, 5> arr2 = {6, 7, 8, 9, 10};
// 2. 元素访问
std::cout << "第一个元素: " << arr.front() << std::endl;
std::cout << "最后一个元素: " << arr.back() << std::endl;
std::cout << "索引为2的元素: " << arr[2] << std::endl;
std::cout << "索引为3的元素(使用at): " << arr.at(3) << std::endl;
std::cout << "数据指针: " << arr.data() << std::endl;
// 3. 容量操作
std::cout << "大小: " << arr.size() << std::endl;
std::cout << "最大大小: " << arr.max_size() << std::endl;
std::cout << "是否为空: " << (arr.empty() ? "是" : "否") << std::endl;
// 4. 迭代器遍历
std::cout << "正向遍历: ";
for (auto it = arr.begin(); it != arr.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
std::cout << "反向遍历: ";
for (auto it = arr.rbegin(); it != arr.rend(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
// 5. 范围for循环
std::cout << "范围for循环: ";
for (const auto& element : arr) {
std::cout << element << " ";
}
std::cout << std::endl;
// 6. 修改操作
arr.fill(42); // 填充
std::cout << "填充后: ";
for (const auto& element : arr) {
std::cout << element << " ";
}
std::cout << std::endl;
// 7. 交换操作
arr.swap(arr2);
std::cout << "交换后arr: ";
for (const auto& element : arr) {
std::cout << element << " ";
}
std::cout << std::endl;
// 8. 使用STL算法
std::sort(arr.begin(), arr.end());
std::cout << "排序后arr: ";
for (const auto& element : arr) {
std::cout << element << " ";
}
std::cout << std::endl;
// 9. 使用get访问元素
std::cout << "第一个元素(get): " << std::get<0>(arr) << std::endl;
// 10. 结构化绑定 (C++17)
auto [a, b, c, d, e] = arr;
std::cout << "结构化绑定: " << a << " " << b << " " << c << " " << d << " " << e << std::endl;
// 11. 编译时计算
constexpr std::array<int, 4> compile_time_arr = []{
std::array<int, 4> result{};
for (int i = 0; i < 4; ++i) {
result[i] = i * i;
}
return result;
}();
std::cout << "编译时数组: ";
for (const auto& elem : compile_time_arr) {
std::cout << elem << " ";
}
std::cout << std::endl;
// 12. 与C风格数组交互
void c_style_function(int* arr, size_t size);
c_style_function(arr.data(), arr.size());
return 0;
}
// 模拟C风格函数
void c_style_function(int* arr, size_t size) {
std::cout << "C风格函数接收: ";
for (size_t i = 0; i < size; ++i) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
}
第一个元素: 1
最后一个元素: 5
索引为2的元素: 3
索引为3的元素(使用at): 4
数据指针: 0x7fff5f773d60
大小: 5
最大大小: 5
是否为空: 否
正向遍历: 1 2 3 4 5
反向遍历: 5 4 3 2 1
范围for循环: 1 2 3 4 5
填充后: 42 42 42 42 42
交换后arr: 6 7 8 9 10
排序后arr: 6 7 8 9 10
第一个元素(get): 6
结构化绑定: 6 7 8 9 10
编译时数组: 0 1 4 9
C风格函数接收: 6 7 8 9 10
实际应用场景
1. 数学计算 - 向量和矩阵
using Vector3 = std::array<float, 3>;
using Matrix3x3 = std::array<std::array<float, 3>, 3>;
Vector3 crossProduct(const Vector3& a, const Vector3& b) {
return {
a[1]*b[2] - a[2]*b[1],
a[2]*b[0] - a[0]*b[2],
a[0]*b[1] - a[1]*b[0]
};
}
2. 游戏开发 - 固定大小数据
struct Transform {
std::array<float, 3> position;
std::array<float, 4> rotation; // 四元数
std::array<float, 3> scale;
};
class GameObject {
private:
std::array<Transform, 100> transforms; // 固定对象池
};
3. 嵌入式系统 - 内存确定
class SensorReader {
private:
std::array<uint16_t, 256> sensor_buffer; // 固定缓冲区
public:
void readData() {
// 无动态分配,适合嵌入式环境
std::fill(sensor_buffer.begin(), sensor_buffer.end(), 0);
}
};
至此,std::array的所有API已完整呈现。从基础构造到高级特性,从元素访问到编译时计算,每一招每一式都已涵盖。掌握这些"术法",配合对连续内存布局"道法"的理解,你已能充分发挥std::array的性能优势,在需要固定大小容器的场景中游刃有余。
上一篇:《C++ STL 容器 -- 道与术 篇(二) list》
相近篇:《C++ STL 容器 -- 道与术 篇(一) vector》
下一篇:《》
总则篇:《C++容器的介绍和使用》--> 怎么选择容器

990

被折叠的 条评论
为什么被折叠?



