vector学习笔记

除了是"动态数组",大小可变 (运行时自动调整),vector更重要的一点是能自动管理内存。

它是一个在栈上的控制者,管理着在堆上的数据。

假设你写了这样一行代码:

cpp 复制代码
// 在栈上声明了一个 vector,并初始化了 3 个元素
    std::vector<int> v = {10, 20, 30};

内存里发生了什么?请看下图:

内存区域 变量/内容 说明
栈 (Stack) 变量 v (控制块) 这里只有 3 个指针(_start, _finish, _end)。 通常占用 24 字节 (64位系统)。 它是自动销毁的。
指向
堆 (Heap) 数据 {10, 20, 30} 真正存数据的地方。 由 v 内部的 allocator 动态申请。 大小取决于元素数量。
cpp 复制代码
#include <iostream>
#include <vector>

int main() {
    // 1. 这是一个局部变量,v 本身在栈上
    std::vector<int> v = {1, 2, 3, 4, 5};

    // 2. 打印 v 这个"对象本身"的地址 (栈)
    std::cout << "Vector 对象本身的地址 (Stack): " << &v << std::endl;

    // 3. 打印 v 管理的"第一个数据"的地址 (堆)
    // v.data() 返回的是堆内存的首地址
    std::cout << "Vector 内部数据的地址 (Heap) : " << v.data() << std::endl;

    // 4. 对比一个典型的栈变量
    int stackVar = 100;
    std::cout << "普通局部变量的地址   (Stack): " << &stackVar << std::endl;
    
    // 5. 对比一个手动 new 出来的堆变量
    int* heapVar = new int(100);
    std::cout << "手动 new 变量的地址  (Heap) : " << heapVar << std::endl;

    delete heapVar;
    return 0;
}
cpp 复制代码
sun@sun-Legion-Y9000P-IAH7H:~/03_My_learn/C++/vector$ g++ test.cpp -o test
sun@sun-Legion-Y9000P-IAH7H:~/03_My_learn/C++/vector$ ./test

输出:

cpp 复制代码
Vector 对象本身的地址 (Stack): 0x7ffdf60fd890
Vector 内部数据的地址 (Heap) : 0x55bdb9f26eb0
普通局部变量的地址   (Stack): 0x7ffdf60fd884
手动 new 变量的地址  (Heap) : 0x55bdb9f272e0

可以看出,Vector 对象本身的地址普通局部变量的地址 距离很近,都在 上;Vector 内部数据的地址手动 new 变量的地址距离很近,都在堆上。

这种栈上控制,堆上存储的设计是 C++ STL 的精髓:

1.栈(Stack)很小且固定 :栈的大小通常只有几 MB(例如 8MB)。如果你试图在栈上创建一个巨大的数组(比如 int arr[10000000]),程序会直接崩溃。

用ulimit -s查看栈内存的最大限制:

cpp 复制代码
sun@sun-Legion-Y9000P-IAH7H:~/03_My_learn/C++/vector$ ulimit -s
8192

约为8M

如果运行下面的程序,会出现段错误。

int arr[10'000'000] 大约需要 10^7 * 4字节 ≈ 40MB。

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

int main() {

    int arr[10'000'000];

    return 0;
}
cpp 复制代码
sun@sun-Legion-Y9000P-IAH7H:~/03_My_learn/C++/vector$ g++ test.cpp -o test
sun@sun-Legion-Y9000P-IAH7H:~/03_My_learn/C++/vector$ ./test
段错误 (核心已转储)

改用vector:

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

int main() {
    
    // 在堆上申请 1000 万个 int
    // v 本身(控制块)在栈上,只占 24 字节。

    std::vector<int> v(10'000'000); 

    // 写入数据证明内存可用
    v[0] = 100;
    v[9999999] = 200;


    return 0; // 退出时,v 析构,自动释放堆内存
}

40MB 的数据被 vector 安排到了堆内存区域。

2.堆(Heap)很大且动态 :堆的大小仅受物理内存限制(几个 GB 甚至 TB)。vector 把数据放在堆上,所以它可以容纳成千上万个元素,而且可以随时扩容

3.自动管理 :虽然数据在堆上(正常需要手动 delete),但控制块 v 在栈上。当超出作用域时,栈上的 v 会自动销毁,v 的析构函数会被触发,进而自动去堆上把那块数据释放掉 。这也是RAII机制的体现。

vector的缺点是存在迭代器失效现象,补充一下vector迭代器失效的几种情况。

1.遍历删除元素,未更新迭代器

cpp 复制代码
/*
* 迭代器失效情况1:遍历删除元素,未更新迭代器
在某些编译器(如 GCC)的特定版本中,vector的erase(iter)实现会将被删除元素之后的所有元素向前移动一位。
*/

#include <iostream>
#include <vector>
using namespace std;
int main()
{
    vector<int> vec = {1};
    for(auto iter = vec.begin(); iter != vec.end(); )
    {
        
    	//if(*iter == 3)
    	cout << *iter << "  ";
    	iter = vec.erase(iter);
    	if(iter!=vec.end())
    	    cout << *iter << endl;
    }
    
    return 0;

}

2.插入新元素触发动态扩容机制,导致内存重新分配,原有的迭代器失效

cpp 复制代码
/*
* 插入新元素触发动态扩容机制,导致内存重新分配,原有的迭代器失效
* 应该在插入操作之后重新获取迭代器
*/
#include <iostream>
#include <vector>
using namespace std;
int main()
{
    vector<int> vec;
    // 验证扩容倍数
    //for(int i = 0; i <= 10; ++i){
    	//vec.emplace_back(i);
    	//cout << "vec的capacity:" << vec.capacity() << endl;
    //}
    vec.reserve(2);
    vec.emplace_back(1);
    vec.emplace_back(2);
    
    auto it = vec.begin();
    
    vec.emplace_back(3);
    
    it = vec.begin();// 插入元素后应该重新获取迭代器
    
    cout << *it << endl;
    


    return 0;
}

3.删除某个元素导致后面元素整体前移

cpp 复制代码
/*
* 删除某个元素导致后面元素整体前移
* 若当前迭代器之前的某个元素被删除,应该在删除后重新计算迭代器的位置
* 
*/
#include <iostream>
#include <vector>

using namespace std;
int main()
{
    vector<int> vec = {1,2,3,4,5,6,8,9};
    
    auto iter = vec.begin() + 5;
    
    cout << *iter << endl;
    
    vec.erase(vec.begin() + 3);
    
    
    
    cout << *iter << endl;

    return 0;
}
相关推荐
MicroTech20252 小时前
区块链赋能,联邦协同:微算法科技(NASDAQ: MLGO)打造物联网安全分布式检测新架构
科技·算法·区块链
(❁´◡`❁)Jimmy(❁´◡`❁)2 小时前
【数据结构】 Treap1: 插入,删除,查找,旋转
数据结构·算法
core5122 小时前
ResNet 残差连接:通往深层网络的“高速公路”
人工智能·算法·resnet
聆风吟º2 小时前
【数据结构手札】顺序表实战指南(五):查找 | 任意位置增删
数据结构·顺序表·查找·任意位置增删
曾几何时`2 小时前
滑动定窗口(十四)2831. 找出最长等值子数组
数据结构·算法
报错小能手2 小时前
数据结构 哈希基础 哈希函数 哈希冲突及解决
数据结构·哈希算法·散列表
柯慕灵2 小时前
轻量推荐算法框架 Torch-rechub——基于PyTorch
pytorch·算法·推荐算法
源代码•宸2 小时前
goframe框架签到系统项目开发(用户认证中间件、实现Refresh-token接口)
数据库·经验分享·后端·算法·中间件·跨域·refreshtoken
YGGP2 小时前
【Golang】LeetCode 300. 最长递增子序列
算法·leetcode