C++ array容器
简介
std::array
是C++11引入的固定大小数组容器,它是对C风格数组的封装,提供了现代C++容器的接口和功能,同时保持了传统数组的性能优势。与标准C数组不同,std::array
知道自己的大小,并支持迭代器、算法和各种有用的成员函数。
备注
std::array
是一个固定大小的容器,一旦创建,其大小就不能改变。如果需要动态大小的数组,应该使用std::vector
。
array的特点
- 大小固定:在编译时确定,不能动态改变
- 连续存储:元素在内存中连续存放,可以进行指针运算
- 直接访问:可以使用
[]
运算符或at()
方法访问元素 - 不会自动管理内存:没有动态内存分配,性能优于
vector
- 支持STL迭代器:可以与STL算法无缝配合
基本语法
声明和初始化
cpp
#include <array>
#include <iostream>
int main() {
// 声明一个包含5个int类型元素的array
std::array<int, 5> arr1;
// 使用初始化列表
std::array<int, 5> arr2 = {1, 2, 3, 4, 5};
// C++11后的统一初始化语法
std::array<int, 5> arr3 = {{1, 2, 3, 4, 5}};
std::array<int, 5> arr4{1, 2, 3, 4, 5};
// 部分初始化,未指定的元素将被初始化为0
std::array<int, 5> arr5 = {1, 2, 3};
// 输出arr5的所有元素
for(const auto& element : arr5) {
std::cout << element << " ";
}
// 输出: 1 2 3 0 0
return 0;
}
常用操作
cpp
#include <array>
#include <iostream>
#include <algorithm>
int main() {
std::array<int, 5> arr = {5, 2, 1, 4, 3};
// 访问元素
std::cout << "第一个元素: " << arr[0] << std::endl; // 不进行边界检查
std::cout << "第二个元素: " << arr.at(1) << std::endl; // 进行边界检查
std::cout << "最后一个元素: " << arr.back() << std::endl; // 获取最后一个元素
std::cout << "第一个元素: " << arr.front() << std::endl; // 获取第一个元素
// 获取大小信息
std::cout << "数组大小: " << arr.size() << std::endl; // 元素个数
std::cout << "数组是否为空: " << arr.empty() << std::endl;// 检查容器是否为空
// 修改元素
arr[0] = 10;
arr.at(1) = 20;
// 排序
std::sort(arr.begin(), arr.end());
// 使用迭代器遍历
std::cout << "排序后的数组: ";
for (auto it = arr.begin(); it != arr.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
// 输出: 排序后的数组: 1 3 4 10 20
// 使用range-based for循环(C++11)
std::cout << "使用range-based for: ";
for (const auto& element : arr) {
std::cout << element << " ";
}
std::cout << std::endl;
// 输出: 使用range-based for: 1 3 4 10 20
// 填充数组
arr.fill(7);
std::cout << "填充后的数组: ";
for (const auto& element : arr) {
std::cout << element << " ";
}
std::cout << std::endl;
// 输出: 填充后的数组: 7 7 7 7 7
return 0;
}
为什么使用std::array而不是C风格数组
C风格数组有一些明显的缺点:
- 不知道自己的大小
- 在函数参数中退化为指针
- 不能复制或赋值
- 没有边界检查,容易出现越界访问
- 没有迭代器支持
相比之下,std::array
提供了更多的安全性和便利性,同时保持了相同的性能水平。
cpp
#include <array>
#include <iostream>
#include <cstring> // 用于C风格数组操作
int main() {
// C风格数组
int c_array[5] = {1, 2, 3, 4, 5};
// std::array
std::array<int, 5> std_array = {1, 2, 3, 4, 5};
// 获取大小
std::cout << "C数组大小需要使用sizeof技巧: " << sizeof(c_array) / sizeof(c_array[0]) << std::endl;
std::cout << "std::array可以直接获取大小: " << std_array.size() << std::endl;
// 边界检查
// c_array[10] = 100; // 危险!越界访问但编译器不会报错
// std_array.at(10) = 100; // 安全!会抛出std::out_of_range异常
// 复制数组
int c_array2[5];
std::memcpy(c_array2, c_array, sizeof(c_array)); // C风格复制
std::array<int, 5> std_array2 = std_array; // 简单赋值即可复制
// 函数传参时的区别
// 在这里不展示,但C数组会退化为指针,而std::array保持类型信息
return 0;
}
与vector的比较
std::array
和std::vector
是两种不同用途的容器:
特性 | std::array | std::vector |
---|---|---|
大小 | 固定 | 动态 |
内存分配 | 栈上(小型数组)或静态存储区 | 堆上 |
扩展能力 | 不能扩展 | 可以动态扩展 |
性能 | 通常更快,无动态分配开销 | 可能有动态分配开销 |
内存布局 | 严格连续 | 连续但可能重新分配 |
适用场景 | 大小固定的集合 | 大小未知或变化的集合 |
cpp
#include <array>
#include <vector>
#include <iostream>
#include <chrono>
int main() {
constexpr int size = 10000;
// 测量std::array性能
auto start1 = std::chrono::high_resolution_clock::now();
std::array<int, size> arr;
for (int i = 0; i < size; ++i) {
arr[i] = i;
}
auto end1 = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::milli> duration1 = end1 - start1;
// 测量std::vector性能
auto start2 = std::chrono::high_resolution_clock::now();
std::vector<int> vec(size);
for (int i = 0; i < size; ++i) {
vec[i] = i;
}
auto end2 = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::milli> duration2 = end2 - start2;
std::cout << "std::array 耗时: " << duration1.count() << " ms" << std::endl;
std::cout << "std::vector 耗时: " << duration2.count() << " ms" << std::endl;
return 0;
}
提示
- 当你确切知道需要存储的元素数量并且该数量不会改变时,使用
std::array
- 当元素数量可能变化或在编译时未知时,使用
std::vector
多维array
std::array
可以嵌套使用来创建多维数组:
cpp
#include <array>
#include <iostream>
int main() {
// 创建3x4的二维数组
std::array<std::array<int, 4>, 3> matrix = {{
{{1, 2, 3, 4}},
{{5, 6, 7, 8}},
{{9, 10, 11, 12}}
}};
// 访问和修改元素
matrix[1][2] = 99;
// 打印二维数组
for (const auto& row : matrix) {
for (const auto& element : row) {
std::cout << element << "\t";
}
std::cout << std::endl;
}
/*
输出:
1 2 3 4
5 6 99 8
9 10 11 12
*/
return 0;
}
实际应用场景
场景1:棋盘游戏
在象棋、围棋等棋盘游戏中,可以使用std::array
表示棋盘:
cpp
#include <array>
#include <iostream>
enum class ChessPiece { EMPTY, PAWN, ROOK, KNIGHT, BISHOP, QUEEN, KING };
int main() {
// 创建8x8的国际象棋棋盘
std::array<std::array<ChessPiece, 8>, 8> chessboard;
// 初始化为空棋盘
for (auto& row : chessboard) {
row.fill(ChessPiece::EMPTY);
}
// 设置一些棋子
chessboard[0][0] = ChessPiece::ROOK;
chessboard[0][1] = ChessPiece::KNIGHT;
chessboard[0][2] = ChessPiece::BISHOP;
// ... 更多初始化设置
// 模拟移动棋子
ChessPiece piece = chessboard[0][1]; // 取出骑士
chessboard[0][1] = ChessPiece::EMPTY; // 原位置变空
chessboard[2][2] = piece; // 移动到新位置
return 0;
}
场景2:图像处理
在处理小型图像或滤镜时,可以使用固定大小的数组:
cpp
#include <array>
#include <iostream>
// 定义3x3的卷积核用于图像边缘检测
std::array<std::array<int, 3>, 3> createSobelKernelX() {
return {{
{{-1, 0, 1}},
{{-2, 0, 2}},
{{-1, 0, 1}}
}};
}
// 应用卷积核到3x3图像块
int applyKernel(const std::array<std::array<int, 3>, 3>& image,
const std::array<std::array<int, 3>, 3>& kernel) {
int result = 0;
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
result += image[i][j] * kernel[i][j];
}
}
return result;
}
int main() {
// 3x3图像区域
std::array<std::array<int, 3>, 3> imageRegion = {{
{{25, 40, 30}},
{{35, 45, 50}},
{{40, 50, 55}}
}};
// 创建Sobel X方向卷积核
auto sobelX = createSobelKernelX();
// 应用卷积计算边缘检测值
int edgeValue = applyKernel(imageRegion, sobelX);
std::cout << "边缘检测值: " << edgeValue << std::endl;
return 0;
}
场景3:RGB颜色表示
使用固定大小的数组表示RGB颜色:
cpp
#include <array>
#include <iostream>
#include <iomanip>
#include <string>
// RGB颜色类型
using Color = std::array<uint8_t, 3>;
// 颜色混合函数
Color mixColors(const Color& color1, const Color& color2, float ratio) {
Color result;
for (size_t i = 0; i < 3; ++i) {
result[i] = static_cast<uint8_t>(color1[i] * (1 - ratio) + color2[i] * ratio);
}
return result;
}
// 将颜色转换为十六进制字符串
std::string toHexString(const Color& color) {
std::stringstream ss;
ss << "#";
for (auto& component : color) {
ss << std::setw(2) << std::setfill('0') << std::hex << static_cast<int>(component);
}
return ss.str();
}
int main() {
Color red = {255, 0, 0};
Color blue = {0, 0, 255};
// 创建紫色 (50% 红色, 50% 蓝色)
Color purple = mixColors(red, blue, 0.5);
std::cout << "红色: " << toHexString(red) << std::endl;
std::cout << "蓝色: " << toHexString(blue) << std::endl;
std::cout << "紫色: " << toHexString(purple) << std::endl;
return 0;
}
总结
std::array
是C++11引入的一个重要容器,它提供了传统C数组的高性能与STL容器的便利性和安全性的完美结合。它适用于需要固定大小数组的场景,特别是那些对性能有较高要求的场景。
主要优点:
- 固定大小,没有动态内存分配的开销
- 支持STL迭代器和算法
- 提供了边界检查功能
- 知道自己的大小
- 可以安全地复制和传递
主要缺点:
- 大小固定,无法动态调整
- 定义非常大的数组可能导致栈溢出(如果是自动存储期)
练习题
-
编写一个函数,接受一个
std::array<int, 10>
参数,将其元素倒序排列并返回一个新的数组。 -
创建一个5x5的二维数组,初始化为单位矩阵(对角线为1,其余为0)。
-
实现一个简单的井字棋游戏,使用
std::array<std::array<char, 3>, 3>
表示棋盘。
额外资源
通过掌握std::array
,你已经迈出了学习现代C++ STL容器的第一步。接下来,你可以继续深入学习其他容器,比如std::vector
、std::list
等,它们在不同场景下各有优势。