【C++】vector详解:接口使用、迭代器、内存理解、与模拟实现

1. 前言

vector 是 C++ 标准模板库(STL)中最常用的动态数组容器之一。提供了一种灵活的方式来存储和管理元素,具备以下特点:

  1. 可变大小数组vector 是一种动态大小的数组容器,能够自动调整存储空间以适应元素增减。

  2. 连续存储:元素在内存中采用连续存储方式,支持高效的随机访问,时间复杂度为 O(1)。

  3. 动态分配:当新元素插入时,如果当前容量不足,vector 会分配一个更大的新数组,并将现有元素复制过去。虽然这个过程代价较高,但通过预留额外空间来减少频繁的重新分配。

  4. 空间管理:不同实现可能采取不同的策略来平衡时间复杂度和空间使用,通常以对数增长的方式增加容量。

  5. 操作效率:在末尾添加和删除元素的时间复杂度为 O(1),而在中间或开头进行插入和删除则为 O(n)。

  6. 迭代器优势vector 的迭代器支持随机访问,提供了比其他序列容器(如 listdeque)更好的性能表现。


2. 内存角度 理解

根据vector的文档介绍,我们知道:

vector是表示大小可变数组的序列容器;,本质是一个封装了动态分配数组的类

如何理解这个容器?

在这里插入图片描述

根据上图,vector申请的空间在堆上,并由标志位记录此时vector的总容量,当容量不够时根据规则进行扩容;


3. vector的使用

定义 | 构造函数

构造函数声明描述
vector()无参构造函数
vector(size_type n, const value_type& val = value_type())构造并初始化 n 个元素为 val
vector(const vector& x)拷贝构造函数
vector(InputIterator first, InputIterator last)使用迭代器范围进行初始化构造

vector iterator

接口说明描述
begin()获取第一个数据位置的 iterator / const_iterator
end()获取最后一个数据的下一个位置的 iterator / const_iterator
rbegin()获取最后一个数据位置的 reverse_iterator
rend()获取第一个数据前一个位置的 reverse_iterator

在这里插入图片描述


vector 空间增长问题

接口说明描述
size()获取数据个数
capacity()获取容量大小
empty()判断是否为空
resize(size_type n)改变 vector 的大小
reserve(size_type n)改变 vector 的容量
  • capacity() 接口 在 vsg++下分别运行会有以下结果:
    • vs下capacity是按1.5倍增长的,g++是按2倍增长的。
    • 即capacity的增长倍数并非固定的,而是根据具体需求定义的
    • vs是PJ版本STL,g++是SGI版本STL。
  • reserve只负责开辟空间,如果确定需要用多少空间,reserve可以缓解vector增容的代价缺陷问题。
  • resize在开空间的同时还会进行初始化,影响size

vector 增删查改

接口说明描述
push_back(const value_type& val)尾部插入元素 val
pop_back()尾部删除最后一个元素
find查找元素(算法模块实现,不是 vector 的成员接口)
insert(iterator position, const value_type& val)在指定位置之前插入元素 val
erase(iterator position)删除指定位置的元素
swap(vector& other)交换两个 vector 的数据空间
operator[](size_type index)像数组一样访问元素

vector 迭代器失效

  • 迭代器作为一种抽象数据类型,使算法在操作不同的数据结构时不必关心这些数据结构的具体实现;

  • 对于vector的迭代器来说,其底层本质是对指针进行了封装(原生态指针T*);因此对于迭代器失效,本质就是指针指向的空间被销毁了,因此指针失效了;

    • 使用被释放的空间,显然结果就是程序崩溃;
    • 即使用失效的迭代器,可能会导致程序崩溃

在使用 std::vector 时,有几种操作可能导致迭代器失效。下面是一些常见的情况及其代码示例:

  1. 插入元素

当向 vector 中插入元素时,如果当前容量不足,vector 会重新分配内存,导致所有迭代器失效。

#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec = {1, 2, 3};
    auto it = vec.begin(); // 保存迭代器

    vec.push_back(4); // 可能导致迭代器失效

    // 使用失效的迭代器
    std::cout << *it << std::endl; // 未定义行为
    return 0;
}
  1. 删除元素

删除元素会导致被删除元素后的所有元素的迭代器失效。

#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec = {1, 2, 3};
    auto it = vec.begin(); // 保存迭代器

    vec.erase(it); // 删除第一个元素,it 现在失效

    // 使用失效的迭代器
    std::cout << *it << std::endl; // 未定义行为
    return 0;
}
  1. 清空容器

调用 clear() 方法将删除 vector 中的所有元素,使得所有指向这些元素的迭代器失效。

#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec = {1, 2, 3};
    auto it = vec.begin(); // 保存迭代器

    vec.clear(); // 清空容器,it 失效

    // 使用失效的迭代器
    std::cout << *it << std::endl; // 未定义行为
    return 0;
}
  1. 重新分配

如果通过改变 vector 的大小(例如 resize)并增加容量,现有迭代器也会失效。

#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec = {1, 2, 3};
    auto it = vec.begin(); // 保存迭代器

    vec.resize(5); // 可能导致迭代器失效

    // 使用失效的迭代器
    std::cout << *it << std::endl; // 未定义行为
    return 0;
}

避免迭代器失效的建议

  • 重新获取迭代器:在进行插入或删除操作后,可以重新获取迭代器。
  • 使用 reserve:如果知道 vector 将要存储的元素数量,可以提前调用 reserve 来避免多次内存分配。
  • 使用范围for循环:尽量使用范围for循环来避免直接操作迭代器。

4. 如何理解 二维动态vector

我们以一个杨辉三角的例子举例,如果想打印高度为n的杨辉三角,可以用二维的vector,比如下面的代码:

void printPascalsTriangle(int numRows) {
    // 创建一个二维向量来存储杨辉三角
    std::vector<std::vector<int>> triangle(numRows);

    // 构建杨辉三角的每一行
    for (int i = 0; i < numRows; ++i) {
        triangle[i].resize(i + 1); // 调整每一行的大小以容纳相应数量的元素
        triangle[i][0] = 1; // 每行的第一个元素设为 1
        triangle[i][i] = 1; // 每行的最后一个元素设为 1

        // 填充当前行的中间元素
        for (int j = 1; j < i; ++j) {
            triangle[i][j] = triangle[i - 1][j - 1] + triangle[i - 1][j]; // 根据杨辉三角性质计算
        }
    }

    // 打印杨辉三角
    for (const auto& row : triangle) {
        for (int num : row) {
            std::cout << num << " "; // 输出当前行的每个数字
        }
        std::cout << std::endl; // 换行
    }
}

在刚初始化二维数组时,是这样的:
在这里插入图片描述
再填入相关数据后,是这样的:

在这里插入图片描述


5. 模拟实现 vector

👇 模拟实现部分在 👇

C++ vector类的模拟实现


6. 相关文档


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值