顺序容器:Array 数组 详解

目录

前言

[一、Array 数组特点](#一、Array 数组特点)

二、定义及初始化

[1.定义 array 对象](#1.定义 array 对象)

2.初始化方式

[三、向 array 对象中添加或删除元素](#三、向 array 对象中添加或删除元素)

1.修改元素

[四、array 常用迭代器](#四、array 常用迭代器)

[五、array 常用运算符](#五、array 常用运算符)

[六、array 常用成员函数](#六、array 常用成员函数)

[1.at 成员函数](#1.at 成员函数)

[2.empty 成员函数](#2.empty 成员函数)

[3.size 成员函数](#3.size 成员函数)

[4.front 成员函数](#4.front 成员函数)

[5.back 成员函数](#5.back 成员函数)

[6.fill 成员函数](#6.fill 成员函数)

[7.swap 成员函数](#7.swap 成员函数)

[七、array 与原生数组的比较](#七、array 与原生数组的比较)

1.优势

2.劣势

[3.示例:array 与原生数组的对比](#3.示例:array 与原生数组的对比)

八、综合练习

[1.练习 1:array 基本操作](#1.练习 1:array 基本操作)

[2.练习 2:使用 array 存储自定义类型](#2.练习 2:使用 array 存储自定义类型)


前言

C++11标准库中的array容器是一种固定大小的顺序容器,具有连续存储、高效性能和类型安全等特点。

它定义在<array>头文件中,大小在编译时确定,不能动态调整。array支持多种初始化方式(默认、列表、聚合、拷贝),提供丰富的迭代器类型(正向、反向、常量)和成员函数(at、empty、size、front、back、fill、swap)。相比原生数组,array具有类型安全、边界检查等优势,但大小固定。文章详细介绍了array的定义、初始化、元素修改、迭代器使用、运算符重载以及与原生数组的比较,并提供了两个综合练习示例。


一、Array 数组特点

array 是 C++11 引入的标准库容器,定义在 <array> 头文件中,位于 std 命名空间内。它是一种固定大小的顺序容器,具有以下特点:

  1. 固定大小 :在编译时确定大小,不能动态扩容或缩容
  2. 连续存储 :元素在内存中连续存储,支持快速随机访问
  3. 高效性能:性能与原生数组相当,同时提供了更多的成员函数和安全检查
  4. 与 STL 兼容:可以使用 STL 算法和迭代器
  5. 类型安全:提供了类型安全的接口,避免了原生数组的一些问题

它是有着固定大小用于保存一系列同类型元素的顺序容器 。因此不能对它进行增加或者删除 ,只能使用或者替换它的元素值。

二、定义及初始化

1.定义 array 对象

使用array,必须包含头文件<array>

cpp 复制代码
#include <array> //使用array,在std命名空间

该类型被定义为一个类模板,在std命名空间中。

cpp 复制代码
template<class T, std::size_t N>
    class array;
cpp 复制代码
#include <array>

// 定义一个包含 5 个 int 元素的 array
std::array<int, 5> arr;

// 定义一个包含 3 个 std::string 元素的 array
std::array<std::string, 3> strArr;

2.初始化方式

  1. 默认初始化:值初始化(对于内置类型,初始化为 0 或空)

    cpp 复制代码
    std::array<int, 5> arr; // 所有元素初始化为 0
    std::array<std::string, 3> strArr; // 所有元素初始化为空字符串
  2. 列表初始化

    cpp 复制代码
    std::array<int, 5> arr = {1, 2, 3, 4, 5};
    std::array<int, 5> arr2{1, 2, 3, 4, 5}; // 与上面等价
  3. 聚合初始化

    cpp 复制代码
    std::array<int, 5> arr = {1, 2, 3}; // 前三个元素初始化为 1, 2, 3,其余元素初始化为 0
  4. 拷贝初始化

    cpp 复制代码
    std::array<int, 5> arr1 = {1, 2, 3, 4, 5};
    std::array<int, 5> arr2 = arr1; // 拷贝初始化
    std::array<int, 5> arr3(arr1); // 拷贝构造

三、向 array 对象中添加或删除元素

由于 array 是固定大小的容器,因此不能vector 那样动态添加或删除元素。所有元素的存储空间在编译时就已经分配好了。

1.修改元素

虽然不能添加或删除元素,但可以修改现有元素的值:

cpp 复制代码
#include <array>

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

// 通过下标修改元素
arr[0] = 10;
arr[1] = 20;

// 通过 at() 成员函数修改元素
arr.at(2) = 30;

// 通过迭代器修改元素
auto it = arr.begin();
*it = 100;
++it;
*it = 200;

四、array 常用迭代器

array 容器提供了多种迭代器类型,用于遍历容器中的元素:

|------------|-----------------------------|
| 迭代器 | 含义 |
| a.begin() | 第一个元素的迭代器 |
| a.end() | 最后一个元素的下一个位置迭代器(尾后迭代器或尾迭代器) |
| a.cbegin() | 第一个元素的常量迭代器(不修改元素内容) |
| a.cend() | 尾后常量迭代器(不修改元素内容) |
| a.rbegin() | 从后往前的第一个迭代器 |
| a.rend() | 从后往前的最后一个迭代器 |

使用示例:

cpp 复制代码
std::array<int, 5> arr = {1, 2, 3, 4, 5};

// 使用正向迭代器遍历
for (auto it = arr.begin(); it != arr.end(); ++it) {
    std::cout << *it << " "; // 输出: 1 2 3 4 5
}
std::cout << std::endl;

// 使用反向迭代器遍历
for (auto it = arr.rbegin(); it != arr.rend(); ++it) {
    std::cout << *it << " "; // 输出: 5 4 3 2 1
}
std::cout << std::endl;

// 使用常量迭代器遍历(不能修改元素)
for (auto it = arr.cbegin(); it != arr.cend(); ++it) {
    std::cout << *it << " "; // 输出: 1 2 3 4 5
    // *it = 10; // 错误:常量迭代器不能修改元素
}
std::cout << std::endl;

五、array 常用运算符

array 类重载了多种运算符,方便容器操作:

  1. 赋值运算符

    • =:将一个 array 赋值给另一个 array(要求大小相同)
    cpp 复制代码
    std::array<int, 5> arr1 = {1, 2, 3, 4, 5};
    std::array<int, 5> arr2;
    arr2 = arr1; // arr2 现在包含 {1, 2, 3, 4, 5}
  2. 比较运算符

    • ==:判断两个 array 是否相等
    • !=:判断两个 array 是否不相等
    • <:判断一个 array 是否小于另一个 array(字典序)
    • <=:判断一个 array 是否小于或等于另一个 array
    • >:判断一个 array 是否大于另一个 array
    • >=:判断一个 array 是否大于或等于另一个 array
    cpp 复制代码
    std::array<int, 3> arr1 = {1, 2, 3};
    std::array<int, 3> arr2 = {1, 2, 4};
    bool b1 = (arr1 == arr2); // false
    bool b2 = (arr1 < arr2);  // true(第三个元素 3 < 4)
  3. 下标运算符

    • []:访问指定位置的元素(不进行边界检查)
    cpp 复制代码
    std::array<int, 5> arr = {10, 20, 30, 40, 50};
    int x = arr[0]; // x = 10
    arr[1] = 25;    // arr 现在包含 {10, 25, 30, 40, 50}

六、array 常用成员函数

|----------------|-----------------------|
| array的成员函数 | 含义 |
| a.at() | 访问指定位置的元素 |
| a.empty() | 判断a是否为空,只能测试长度为0(不重要) |
| a.size() | 返回数组长度 |
| a.front() | 返回第一个元素的引用 |
| a.back() | 返回最后一个元素的引用 |
| a.fill(val) | 将val赋值给每个元素 |
| a.swap() | 交换两个array的值 |

1.at 成员函数

  • 功能:访问指定位置的元素(进行边界检查)

  • 参数:元素的索引位置

  • 返回值:指定位置的元素的引用

  • 异常 :如果索引超出范围,抛出 out_of_range 异常

    std::array<int, 5> arr = {10, 20, 30, 40, 50};
    int x = arr.at(0); // x = 10
    arr.at(1) = 25; // arr 现在包含 {10, 25, 30, 40, 50}

    try {
    x = arr.at(10); // 抛出 out_of_range 异常
    } catch (const std::out_of_range& e) {
    std::cout << "Exception: " << e.what() << std::endl;
    }

2.empty 成员函数

  • 功能:判断 array 是否为空
  • 参数:无
  • 返回值 :如果 array 为空返回 true,否则返回 false
  • 说明 :对于非空大小的 array,始终返回 false
cpp 复制代码
std::array<int, 0> emptyArr; // 空 array
std::array<int, 5> nonEmptyArr = {1, 2, 3, 4, 5};

std::cout << emptyArr.empty();    // 输出: 1 (true)
std::cout << nonEmptyArr.empty(); // 输出: 0 (false)

3.size 成员函数

  • 功能:返回 array 中元素的个数
  • 参数:无
  • 返回值 :元素个数,类型为 size_t
  • 说明:返回的是编译时确定的大小,与初始化时的大小相同
cpp 复制代码
std::array<int, 5> arr = {1, 2, 3, 4, 5};
std::cout << arr.size(); // 输出: 5

std::array<double, 10> dblArr;
std::cout << dblArr.size(); // 输出: 10

4.front 成员函数

  • 功能:返回 array 中第一个元素的引用
  • 参数:无
  • 返回值:第一个元素的引用
  • 说明:如果 array 为空,行为未定义
cpp 复制代码
std::array<int, 5> arr = {1, 2, 3, 4, 5};
std::cout << arr.front(); // 输出: 1
arr.front() = 10;         // arr 现在包含 {10, 2, 3, 4, 5}

5.back 成员函数

  • 功能:返回 array 中最后一个元素的引用
  • 参数:无
  • 返回值:最后一个元素的引用
  • 说明:如果 array 为空,行为未定义
cpp 复制代码
std::array<int, 5> arr = {1, 2, 3, 4, 5};
std::cout << arr.back(); // 输出: 5
arr.back() = 50;         // arr 现在包含 {1, 2, 3, 4, 50}

6.fill 成员函数

  • 功能:用指定值填充 array 中的所有元素
  • 参数:要填充的值
  • 返回值:无
cpp 复制代码
std::array<int, 5> arr = {1, 2, 3, 4, 5};
arr.fill(0); // arr 现在包含 {0, 0, 0, 0, 0}

std::array<std::string, 3> strArr;
strArr.fill("hello"); // strArr 现在包含 {"hello", "hello", "hello"}

7.swap 成员函数

  • 功能:交换两个 array 的内容

  • 参数:另一个 array 对象(必须与当前 array 大小相同)

  • 返回值:无

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

    arr1.swap(arr2); // arr1 现在包含 {4, 5, 6},arr2 现在包含 {1, 2, 3}

七、array 与原生数组的比较

1.优势

  1. 类型安全array 是一个模板类,提供了类型安全的接口
  2. 边界检查at() 成员函数提供了边界检查
  3. STL 兼容:可以使用 STL 算法和迭代器
  4. 大小信息 :通过 size() 成员函数可以获取大小
  5. 赋值操作:支持直接赋值,而原生数组不支持

2.劣势

  1. 大小固定:编译时确定大小,不能动态调整
  2. 稍微的性能开销:相比原生数组,有轻微的性能开销(通常可以忽略)

3.示例:array 与原生数组的对比

cpp 复制代码
#include <iostream>
#include <array>
#include <algorithm>

int main() {
    // 原生数组
    int nativeArr[5] = {5, 2, 8, 1, 9};
    
    // array 容器
    std::array<int, 5> arr = {5, 2, 8, 1, 9};
    
    // 原生数组的大小需要使用 sizeof 计算
    std::cout << "Native array size: " << sizeof(nativeArr) / sizeof(nativeArr[0]) << std::endl;
    
    // array 容器直接通过 size() 获取大小
    std::cout << "Array container size: " << arr.size() << std::endl;
    
    // 原生数组不能直接赋值
    // int anotherNativeArr[5];
    // anotherNativeArr = nativeArr; // 错误
    
    // array 容器可以直接赋值
    std::array<int, 5> anotherArr;
    anotherArr = arr;
    
    // 都可以使用范围 for 循环
    std::cout << "Native array elements: ";
    for (int x : nativeArr) {
        std::cout << x << " ";
    }
    std::cout << std::endl;
    
    std::cout << "Array container elements: ";
    for (int x : arr) {
        std::cout << x << " ";
    }
    std::cout << std::endl;
    
    // 都可以使用 STL 算法
    std::sort(nativeArr, nativeArr + 5);
    std::sort(arr.begin(), arr.end());
    
    std::cout << "Sorted native array: ";
    for (int x : nativeArr) {
        std::cout << x << " ";
    }
    std::cout << std::endl;
    
    std::cout << "Sorted array container: ";
    for (int x : arr) {
        std::cout << x << " ";
    }
    std::cout << std::endl;
    
    return 0;
}

八、综合练习

1.练习 1:array 基本操作

cpp 复制代码
#include <iostream>
#include <array>

int main() {
    // 创建并初始化 array
    std::array<int, 5> arr = {1, 2, 3, 4, 5};
    
    // 输出原始 array
    std::cout << "Original array: ";
    for (int x : arr) {
        std::cout << x << " ";
    }
    std::cout << std::endl;
    
    // 修改元素
    arr[0] = 10;
    arr.at(1) = 20;
    arr.back() = 50;
    
    // 输出修改后的 array
    std::cout << "Modified array: ";
    for (int x : arr) {
        std::cout << x << " ";
    }
    std::cout << std::endl;
    
    // 使用 fill 填充
    arr.fill(99);
    
    // 输出填充后的 array
    std::cout << "After fill: ";
    for (int x : arr) {
        std::cout << x << " ";
    }
    std::cout << std::endl;
    
    // 创建另一个 array 并交换
    std::array<int, 5> arr2 = {100, 200, 300, 400, 500};
    arr.swap(arr2);
    
    // 输出交换后的 array
    std::cout << "After swap - arr: ";
    for (int x : arr) {
        std::cout << x << " ";
    }
    std::cout << std::endl;
    
    std::cout << "After swap - arr2: ";
    for (int x : arr2) {
        std::cout << x << " ";
    }
    std::cout << std::endl;
    
    // 输出 array 信息
    std::cout << "Size: " << arr.size() << std::endl;
    std::cout << "Empty: " << (arr.empty() ? "Yes" : "No") << std::endl;
    std::cout << "Front: " << arr.front() << std::endl;
    std::cout << "Back: " << arr.back() << std::endl;
    
    return 0;
}

2.练习 2:使用 array 存储自定义类型

cpp 复制代码
#include <iostream>
#include <array>
#include <string>

class Person {
public:
    std::string name;
    int age;
    
    Person(std::string n = "", int a = 0) : name(n), age(a) {}
    
    void display() const {
        std::cout << "Name: " << name << ", Age: " << age << std::endl;
    }
};

int main() {
    // 创建存储 Person 对象的 array
    std::array<Person, 3> people = {
        Person("Alice", 25),
        Person("Bob", 30),
        Person("Charlie", 35)
    };
    
    // 遍历并显示所有元素
    std::cout << "All people: " << std::endl;
    for (const auto& person : people) {
        person.display();
    }
    
    // 修改元素
    people[0].name = "Alicia";
    people.at(1).age = 31;
    
    // 显示修改后的元素
    std::cout << "\nAfter modification: " << std::endl;
    for (const auto& person : people) {
        person.display();
    }
    
    // 使用 fill 填充
    people.fill(Person("Unknown", 0));
    
    // 显示填充后的元素
    std::cout << "\nAfter fill: " << std::endl;
    for (const auto& person : people) {
        person.display();
    }
    
    return 0;
}
相关推荐
qq_392807952 小时前
Qt 注册 C++ 给 QML 调用的几种方式
数据库·c++·qt
宵时待雨2 小时前
C++笔记归纳15:红黑树
开发语言·数据结构·c++·笔记
具身小佬2 小时前
两轴机械臂,ros2上位机控制,直接输入坐标或者键盘控制,can通信控制
c++·ubuntu
cccyi72 小时前
【C++ 脚手架】Jsoncpp 库的介绍与使用
c++·optional·jsoncpp
Yupureki2 小时前
《Linux系统编程》16.进程间通信-共享内存
linux·运维·服务器·c语言·数据结构·c++
看山是山_Lau3 小时前
如何封装和定义一个函数
c语言·开发语言·c++·笔记
-许平安-3 小时前
MCP项目笔记五(PluginAPI)
c++·笔记·rpc·json·mcp·pluginapi
C_Si沉思3 小时前
C++与硬件交互编程
开发语言·c++·算法
tankeven3 小时前
HJ148 迷宫寻路
c++·算法