C++ STL array 完全指南

张开发
2026/4/11 8:04:27 15 分钟阅读

分享文章

C++ STL array 完全指南
目录C STL 容器详解std::array 完全指南一、概述二、类模板参数三、初始化方法3.1 默认初始化3.2 聚合初始化推荐3.3 拷贝/移动构造3.4 重新赋值赋值运算符四、元素获取函数4.1 元素引用at( )4.2 元素引用下标运算符[ ]4.3 首元素引用front( )4.4 尾元素引用back( )4.5 数组首地址data( )五、迭代器5.1 array迭代器运算符1. 解引用运算符* , -2. 递增/递减运算符: , --3. 算术运算符: , - , , -4. 下标运算符 : [ ]5. 比较运算符: , ! , , , , 5.2 正向迭代器begin() / end()5.3 常量正向迭代器cbegin() / cend()5.4 反向迭代器rbegin() / rend()5.5 常量反向迭代器crbegin() / crend()六、其他成员函数6.1 元素个数size( )6.2 素数个数max_size( )6.3 判断空empty( )6.4 所有元素赋值fill( )6.5 交换对象元素swap( )七、非成员函数7.1 比较运算符, !, , , , 7.2 编译期元素引用get( )八、总结8.1. 与原生数组的对比8.2. 性能与边界安全最佳实践8.3 特点总结C STL 容器详解std::array 完全指南固定大小数组的现代 C 封装兼顾性能与安全一、概述std::array是 C11 标准库中引入的容器它封装了固定大小的原生数组提供了 STL 容器的通用接口如迭代器、大小查询、边界检查等同时保持了原生数组的零开销特性。头文件 array名字空间std核心特点数据结构数组空间连续大小固定。固定大小对象定义时大小确定跟普通的数组一样性质不可动态增长或缩减。连续存储元素在内存中连续排列兼容 C 风格 API通过data()获取首地址。零开销性能与原生数组完全相同不引入额外运行时负担。即数据个数多于数组元素数量越界。二、类模板参数类模板声明template class T, std::size_t N struct array;参数说明T元素类型N元素个数编译期常量类型为std::size_tstd::array的内部实现通常类似于templatetypename T, size_t N struct array { T _M_elems[N]; // 原生数组 // ... 成员函数 };由于是聚合类型没有虚函数、没有私有成员因此对象大小就是N * sizeof(T)。三、初始化方法3.1 默认初始化std::arrayint, 5 arr1; std::arrayint, 5 arr2 {};arr1默认构造函数未指定初始值元素值不确定局部变量arr2全部元素零初始化0 0 0 0 03.2 聚合初始化推荐聚合初始化即大括号{ }形式初始化保持原生数组的特点。int a[5] {1,2,3,4,5};方式1等号 花括号初始化部分元素剩余元素默认初始化0std::arrayint, 5 a1 {1, 2, 3};a11 2 3 0 0初始化部分元素剩余元素默认初始化0。方式2直接花括号C14起单层即可std::arrayint, 3 a2 {1, 2, 3}; // 1 2 3 ​​​​​​​std::arrayint, 3 a3 {{1, 2, 3}}; // 1 2 3C11使用双层大括号C14后单层双层均可一般不用双层大括号麻烦。方式3二维数组3 x 2。3行2列。std::arraystd::arrayint, 2, 3 a4 {1,2,3,4,5,6};1 23 45 63.3 拷贝/移动构造函数原型array() noexcept; //默认构造 array(const array other); //拷贝构造 array(array other) noexcept; //移动构造示例std::arrayint, 4 a1 {1,2,3,4}; // 聚合构造 std::arrayint, 4 a2 src; // 拷贝构造 std::arrayint, 4 a3 std::move(src); // 移动构造元素逐移动3.4 重新赋值赋值运算符std::arrayint, 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::arrayint, 6 a1 { 1,2,3,4,5,6 }; cout a1.at(2) endl; // 3 a1.at(2) 10; // 1,10,3,4,5,6 // a1.at(7) 56; // 越界严重错误 std::arraystd::arrayint, 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::arrayint, 6 a1 { 1,2,3,4,5,6 }; cout a1[2] endl; // 3 a1[2] 10; // 1,10,3,4,5,6 // a1[7] 56; // 越界严重错误 std::arraystd::arrayint, 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::arrayint, 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; // 104.4 尾元素引用back( )函数原型T back(); const T back() const;示例std::arrayint, 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; // 104.5 数组首地址data( )返回指向底层数组的首地址。函数原型T* data() noexcept; const T* data() const noexcept;示例std::arrayint, 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::arrayint, 6 a { 1,2,3,4,5,6 }; std::arrayint, 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::arrayint, 6 a { 1,2,3,4,5,6 }; std::arrayint, 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::arrayint, 6 a { 1,2,3,4,5,6 }; std::arrayint, 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::arrayint, 6 a { 1,2,3,4,5,6 }; std::arrayint, 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::arrayint, 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::arrayint, 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::arrayint, 6 a { 1,2,3,4,5,6 }; cout a.empty() endl; // 0 非空 std::arrayint, 0 d; cout d.empty() endl; // 1 空 return 0; }6.4 所有元素赋值fill( )函数原型将数组所有元素设置为value void fill( const T value );示例int main(void) { std::arrayint, 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::arrayint, 6 a { 1,2,3,4,5,6 }; std::arrayint, 6 c { 8,8,8,8,8,8 }; a.swap(c); // 元素交换七、非成员函数7.1 比较运算符, !, , , , 函数原型template class T, std::size_t N bool operator( const std::arrayT, N lhs,const std::arrayT, N rhs ); template class T, std::size_t N bool operator!( const std::arrayT, N lhs,const std::arrayT, N rhs ); template class T, std::size_t N bool operator ( const std::arrayT, N lhs,const std::arrayT, N rhs ); template class T, std::size_t N bool operator( const std::arrayT, N lhs,const std::arrayT, N rhs ); template class T, std::size_t N bool operator ( const std::arrayT, N lhs,const std::arrayT, N rhs ); template class T, std::size_t N bool operator( const std::arrayT, N lhs,const std::arrayT, N rhs );示例std::arrayint,3 a {1,2,3}; std::arrayint,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::arrayNode, 3 a { 1,2, 3,4, 5,6}; std::arrayNode, 3 c { 8,8, 8,8, 8,8 }; cout (a c) endl; return 0; }7.2 编译期元素引用get( )延伸阅读C17 添加了结构化绑定可与std::array配合使用。C20 的std::to_array进一步简化了创建过程。拥抱现代 C从使用std::array开始函数原型namespace std { // 获取普通左值引用 templatesize_t I, class T, size_t N constexpr T get(arrayT,N arr) noexcept; // 获取 const 左值引用 templatesize_t I, class T, size_t N constexpr const T get(const arrayT,N arr) noexcept; // 获取右值引用移动语义 templatesize_t I, class T, size_t N constexpr T get(arrayT,N arr) noexcept; // 获取 const 右值引用极少使用 templatesize_t I, class T, size_t N constexpr const T get(const arrayT,N arr) noexcept; }示例int main(void) { std::arrayint, 6 a { 1,2, 3,4, 5,6}; get1(a) 10; // 赋值 int d get1(a); return 0; }关键特性特性说明编译期索引索引 必须是编译期常量如字面量、constexpr变量不能是运行时变量边界检查索引越界I N会导致编译错误而非运行时异常返回值类型根据arr的值类别返回相应的引用左值、const 左值、右值引用与[]/at()的区别operator[]使用运行时索引无边界检查越界运行异常at(i)运行时索引需要边界检查std::get使用编译期索引越界直接编译失败noexcept始终不抛出异常5.3std::to_array(C20)从内建数组或初始化列表创建std::array自动推导大小。函数原型template class T, std::size_t N constexpr std::arraystd::remove_cv_tT, N to_array( T (a)[N] ); template class T, std::size_t N constexpr std::arraystd::remove_cv_tT, N to_array( T (a)[N] );示例int main() { // 从 C 风格数组创建 int carr[] {1, 2, 3, 4}; auto arr1 std::to_array(carr); // std::arrayint, 4 // 从字面量创建字符串 auto arr2 std::to_array(hello); // std::arraychar, 6 包含 \0 // 从初始化列表创建需要显式指定类型 auto arr3 std::to_arrayint({10, 20, 30}); // std::arrayint, 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::arrayT,N。8.3 特点总结适用场景说明✅ 需要固定大小的数组编译期已知元素个数✅ 需要与 STL 算法无缝协作迭代器接口完备✅ 需要边界检查的可选支持at()成员函数✅ 追求零开销抽象性能与原生数组完全相同❌ 需要动态扩容请使用std::vector❌ 大小在运行时才能确定请使用std::vector或动态分配std::array填补了原生数组和std::vector之间的空白是现代 C 中固定大小数组的首选类型。尽量用它替代原生数组以获得类型安全、大小信息自包含以及丰富的成员函数支持。

更多文章