c++ 动态数组( std::vector)

目录

为什么有std::vector?

什么是 std::vector?

 vector 的核心设计理念(用类思维理解)

动态数组的底层结构

动态数组的基本操作

创建和初始化

 插入元素

删除元素 

 访问元素

 容量与大小

插入特定位置的元素:

删除指定位置的元素:

清空数组:

调整容量: 

终极总结一句话:


为什么有std::vector

int arr[10];   // 固定大小数组

 这个东西用起来很简单,但它有几个硬伤:

  • 大小固定:你不能动态增加元素。

  • 不能自动扩容:想插入第 11 个元素?麻烦了。

  • 没有安全边界检查:arr[100] 也不会报错,直接 UB(undefined behavior)。

  • 不能直接赋值、复制、比较。

  • 缺少高级接口:没有 .size().push_back().insert() 等等。

 于是,我们问一个根本性的问题:

有没有一个可以自动扩容、安全、易用、功能丰富的“数组”? 

C++ 标准库说:有,那就是 —— std::vector 

 

什么是 std::vector

std::vector<T> 是 C++ 标准库提供的一个 动态数组模板类,能在运行时自动管理内存、调整大小、提供各种操作方法。 

你可以理解它是 C++ 中的“可变长数组”,但背后封装了大量复杂逻辑。

 vector 的核心设计理念(用类思维理解)

template<typename T>
class vector {
private:
    T* data;           // 元素数组(堆上分配)
    size_t size;       // 当前元素个数
    size_t capacity;   // 当前分配的最大容量
public:
    void push_back(const T& val);
    void pop_back();
    void resize(size_t new_size);
    void reserve(size_t new_capacity);
    T& operator[](size_t index);
    size_t size() const;
    // ...
};

 vector 就是自己在内部维护了一个 堆上的数组,并且提供各种方法自动管理这块内存。

 

动态数组的底层结构

std::vector 内部就像是这样: 

[ 0 ][ 1 ][ 2 ][ 3 ][ 4 ]   ← 一整块连续内存

动态数组背后的核心思想是,当数组容量用尽时,它会自动扩展。这是通过以下几个步骤实现的:

  1. 初始分配内存:当你创建一个动态数组时,它会分配一块初始的内存空间。例如,在 C++ 中,std::vector 默认会分配一个固定的大小(比如 0 或 1)。

  2. 增加元素时的扩展机制:当数组中的元素超出当前分配的空间时,动态数组会进行扩展。常见的做法是将数组的容量增加为原来的两倍。这样,数组容量的增加会使得扩容的次数减少,从而提升性能。

  3. 元素复制和内存释放:扩容时,程序会申请一个新的更大的内存块,然后将旧数组中的元素复制到新数组中。最后,释放掉旧数组的内存。

  4. 删除元素时的收缩机制:虽然扩展通常是自动的,但收缩则不总是自动进行的。在某些情况下,动态数组可能会减少其容量以释放内存,特别是在元素数量显著减少时。

  • 所有元素挨在一起

  • 优点:随机访问非常快(因为地址是线性的)

  • 缺点:

    • 如果你在开头插入一个元素,就要把所有元素后移 → 开销大


动态数组的基本操作

操作功能底层发生了什么
push_back(x)尾部添加元素空间足够就加,不够就扩容
pop_back()删除最后一个元素只是 size--,不释放内存
size()返回当前元素个数O(1),不等于 capacity()
capacity()返回总容量看看当前还能装多少
reserve(n)预留容量提前分配内存,避免频繁扩容
resize(n)修改逻辑大小增大则构造新元素,减小则析构多余
clear()清空元素size = 0,但内存还在
operator[]访问元素没有边界检查,风险!
at(i)访问元素有边界检查,超出抛异常

创建和初始化

#include <iostream>
#include <vector>

int main() {
    // 创建一个空的动态数组
    std::vector<int> arr;

    // 创建一个指定大小和初始值的动态数组
    std::vector<int> arr2(5, 10);  // 创建一个包含5个元素,每个元素初始值为10的数组

    // 创建一个带有初始值的动态数组
    std::vector<int> arr3 = {1, 2, 3, 4, 5};

    // 输出 arr3 的内容
    for (int i = 0; i < arr3.size(); ++i) {
        std::cout << arr3[i] << " ";
    }

    return 0;
}

 插入元素

#include <iostream>
#include <vector>

int main() {
    std::vector<int> arr;

    // 向数组末尾添加元素
    arr.push_back(10);
    arr.push_back(20);
    arr.push_back(30);

    // 输出数组内容
    for (int i = 0; i < arr.size(); ++i) {
        std::cout << arr[i] << " ";
    }

    return 0;
}

删除元素 

#include <iostream>
#include <vector>

int main() {
    std::vector<int> arr = {10, 20, 30};

    // 删除最后一个元素
    arr.pop_back();

    // 输出删除后的数组内容
    for (int i = 0; i < arr.size(); ++i) {
        std::cout << arr[i] << " ";
    }

    return 0;
}

 访问元素

#include <iostream>
#include <vector>

int main() {
    std::vector<int> arr = {10, 20, 30};

    // 使用下标访问元素
    std::cout << "First element: " << arr[0] << std::endl;

    // 使用 at() 方法访问元素(带边界检查)
    std::cout << "Second element: " << arr.at(1) << std::endl;

    return 0;
}

 容量与大小

#include <iostream>
#include <vector>

int main() {
    std::vector<int> arr = {10, 20, 30};

    // 获取元素个数
    std::cout << "Size: " << arr.size() << std::endl;

    // 获取容量(当前能够容纳的元素个数)
    std::cout << "Capacity: " << arr.capacity() << std::endl;

    return 0;
}

插入特定位置的元素

使用 insert() 方法可以在特定位置插入元素。例如:

arr.insert(arr.begin() + 1, 15);  // 在索引为1的位置插入15

删除指定位置的元素

使用 erase() 方法可以删除特定位置的元素。

arr.erase(arr.begin() + 1);  // 删除索引为1的元素

清空数组

使用 clear() 方法可以删除所有元素,清空数组。

arr.clear();

调整容量: 

1.reserve() 方法

用于预先分配至少 n 个元素的空间。它不会改变当前存储的元素个数(即数组的 size()),但会确保至少有 n 个元素的内存空间可用。如果当前容量小于 n,则会分配更多的内存。如果 n 小于或等于当前容量,则不会做任何操作。

vector.reserve(n);

n:指定所需的最小容量。这个容量是指 vector 至少能够容纳 n 个元素,而不需要重新分配内存。 

注意reserve() 只是调整容量,而不会影响 size(),所以它不会增加现有元素的数量。

示例:

#include <iostream>
#include <vector>

int main() {
    std::vector<int> arr;

    // 初始容量为0,reserve(10)会将容量预留为至少10个元素
    arr.reserve(10);

    // 检查容量
    std::cout << "Capacity after reserve: " << arr.capacity() << std::endl;

    // 向数组中添加元素
    arr.push_back(1);
    arr.push_back(2);

    // 检查大小和容量
    std::cout << "Size: " << arr.size() << ", Capacity: " << arr.capacity() << std::endl;

    return 0;
}

输出: 

Capacity after reserve: 10
Size: 2, Capacity: 10

2.shrink_to_fit() 方法

请求将 std::vector 的容量减少到与当前 size() 相等。即它会释放多余的内存空间,优化内存使用。需要注意的是,shrink_to_fit() 只是一个请求操作,实际是否进行内存调整取决于实现,可能并不会立刻发生。

语法:

vector.shrink_to_fit();

注意shrink_to_fit() 并不会直接改变 size(),它只会尝试调整 vector 的容量,使其更接近当前的元素数量。 

示例:

#include <iostream>
#include <vector>

int main() {
    std::vector<int> arr;

    // 添加一些元素
    arr.push_back(1);
    arr.push_back(2);
    arr.push_back(3);

    // 查看当前容量
    std::cout << "Before shrink: Size = " << arr.size() << ", Capacity = " << arr.capacity() << std::endl;

    // 调用 shrink_to_fit() 请求减少容量
    arr.shrink_to_fit();

    // 查看调用后的大小和容量
    std::cout << "After shrink: Size = " << arr.size() << ", Capacity = " << arr.capacity() << std::endl;

    return 0;
}

输出: 

Before shrink: Size = 3, Capacity = 4
After shrink: Size = 3, Capacity = 3

终极总结一句话:

vector 是 C++ 提供的“自动化动态数组”,本质是用堆内存 + 封装管理 + 自动扩容,实现了内存安全与操作便利的平衡。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值