【std::vector】vector<T*>与vector<T>*

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

      • [一、先回答第一个问题:`vector<int*>` 类型拷贝前,是否需要遍历一遍元素 `delete`?](#一、先回答第一个问题:vector<int*> 类型拷贝前,是否需要遍历一遍元素 delete?)
        • [1. 核心前提:`vector<int*>` 只存指针,不管理指针指向的内存(浅拷贝特性)](#1. 核心前提:vector<int*> 只存指针,不管理指针指向的内存(浅拷贝特性))
        • [2. 分情况讨论:是否需要`delete`](#2. 分情况讨论:是否需要delete)
        • [3. 优化方案:用智能指针代替裸指针(推荐)](#3. 优化方案:用智能指针代替裸指针(推荐))
      • [二、`vector<int*>` 与 `vector<int>*` 的对比](#二、vector<int*>vector<int>* 的对比)
        • [1. 栈堆分布:分层拆解(关键:区分"对象本身"和"对象指向/存储的内容")](#1. 栈堆分布:分层拆解(关键:区分“对象本身”和“对象指向/存储的内容”))
          • [(1)`vector<int*>` 的栈堆分布(三层结构)](#(1)vector<int*> 的栈堆分布(三层结构))
          • [(2)`vector<int>*` 的栈堆分布(三层结构)](#(2)vector<int>* 的栈堆分布(三层结构))
        • [2. 适用场景](#2. 适用场景)
          • [(1)`vector<int*>` 的使用场景](#(1)vector<int*> 的使用场景)
          • [(2)`vector<int>*` 的使用场景](#(2)vector<int>* 的使用场景)
        • [3. 核心区别总结](#3. 核心区别总结)
      • 三、补充示例代码
        • [1. `vector<int*>` 的内存管理示例(裸指针版,需手动delete)](#1. vector<int*> 的内存管理示例(裸指针版,需手动delete))
        • [2. `vector<int>*` 的示例(现代C++推荐用unique_ptr)](#2. vector<int>* 的示例(现代C++推荐用unique_ptr))

一、先回答第一个问题:vector<int*> 类型拷贝前,是否需要遍历一遍元素 delete

结论:不一定,核心取决于vector<int*>中存储的指针指向的内存类型,以及谁是这些内存的所有者

1. 核心前提:vector<int*> 只存指针,不管理指针指向的内存(浅拷贝特性)

vector<int*> 中的元素是**int*类型的指针**,vector的赋值/拷贝操作只会做浅拷贝 :即只复制指针的 (也就是内存地址),不会复制指针指向的int对象,也不会自动管理指针指向的内存。

因此,vector的默认行为不会帮你delete指针指向的内存------这部分内存的生命周期需要程序员手动控制。

2. 分情况讨论:是否需要delete
情况1:指针指向栈内存(或全局/静态内存)

如果vector<int*>中的指针指向栈上的int(比如int a = 10; int* p = &a;),绝对不能delete

栈内存由编译器自动管理,delete栈指针会导致未定义行为(程序崩溃、内存错乱等)。

情况2:指针指向堆内存new int/ new int[]分配的)

如果指针是通过new/new[]分配的堆内存(比如int* p = new int(5);),则需要分场景:

  • 场景A:当前vector是该堆内存的唯一所有者 (没有其他指针指向这块内存):
    在拷贝(赋值、清空、析构)前,必须遍历vector执行delete(或delete[] ,否则指针指向的堆内存会成为"孤儿内存",导致内存泄漏
  • 场景B:堆内存被多个指针共享 (比如其他变量也指向这块内存):
    不能直接delete,否则会导致其他指针变成悬垂指针(指向已释放的内存),后续访问会触发未定义行为。
3. 优化方案:用智能指针代替裸指针(推荐)

手动delete裸指针容易出错(漏删、重复删、删栈指针),C++11起推荐用智能指针unique_ptr/shared_ptr)代替裸指针,智能指针会自动管理内存,无需手动delete

cpp 复制代码
// 用unique_ptr(独占所有权)代替裸指针,自动释放内存
vector<unique_ptr<int>> v1;
v1.push_back(make_unique<int>(5));
v1.push_back(make_unique<int>(6));

// 赋值时,unique_ptr会自动转移所有权,旧的unique_ptr会自动释放内存(无需手动delete)
vector<unique_ptr<int>> v2;
v1 = v2; // 旧的v1元素(unique_ptr)会被销毁,自动delete指向的int

二、vector<int*>vector<int>* 的对比

首先明确两者的类型本质,这是理解后续内容的关键:

类型 本质含义
vector<int*> 一个vector容器对象 ,其存储的元素是int*类型的指针 (指向int的指针)
vector<int>* 一个指针变量 ,其指向的是vector<int>类型的容器对象

接下来从栈堆分布使用场景核心区别三个维度详细分析。

1. 栈堆分布:分层拆解(关键:区分"对象本身"和"对象指向/存储的内容")

C++中内存分布的核心规则:局部变量(非static)在栈上,new/malloc分配的在堆上 。但vector的底层特性是:无论vector对象本身在栈还是堆,其内部存储的元素数组永远在堆上(vector的动态数组是动态分配的)。

(1)vector<int*> 的栈堆分布(三层结构)
  • 第一层:vector<int*> 对象本身
    取决于声明方式:
    • 局部变量:vector<int*> v; → 对象本身在上(栈帧中)。
    • 动态分配:vector<int*> *pv = new vector<int*>; → 对象本身在 上(new分配)。
  • 第二层:vector<int*> 内部的元素数组(存储int*指针的数组)
    无论vector对象本身在栈还是堆,这部分数组永远在上(vector的底层实现决定)。
  • 第三层:int*指针指向的int对象
    由指针的赋值决定:
    • 指向栈:int a = 10; int* p = &a;int在栈上。
    • 指向堆:int* p = new int(10);int在堆上。
(2)vector<int>* 的栈堆分布(三层结构)
  • 第一层:vector<int>* 指针变量本身
    取决于声明方式:
    • 局部变量:vector<int>* pv; → 指针变量本身在上(栈帧中,占用8字节(64位)存储内存地址)。
    • 动态分配:vector<int>** ppv = new vector<int>*; → 指针变量本身在上(极少用)。
  • 第二层:指针指向的vector<int>对象
    几乎只能在堆上 (否则无意义,且易出错):
    • 正确用法:pv = new vector<int>;vector<int>对象在堆上。
    • 错误用法:vector<int> v; pv = &v;pv指向栈上的vector,若v先销毁,pv会变成悬垂指针(后续访问崩溃)。
  • 第三层:vector<int> 对象内部的int元素数组
    永远在上(vector的底层特性)。
2. 适用场景
(1)vector<int*> 的使用场景

用于需要存储多个指向int的指针的场景,常见情况:

  • 场景1:管理一组独立的动态int对象/数组
    比如需要存储多个大小不同的int数组(int* arr1 = new int[5]; int* arr2 = new int[10];),可以用vector<int*>存储这些数组的首地址。
  • 场景2:与C风格代码交互
    C函数常返回int*类型的数组(如int* get_data()),可以用vector<int*>接收和管理这些指针。
  • 场景3:存储指向不同内存位置的int
    比如同时指向栈上的int、全局int、堆上的int

注意:尽量用vector<unique_ptr<int>>/vector<shared_ptr<int>>代替裸指针的vector<int*>,避免手动管理内存的风险。

(2)vector<int>* 的使用场景

这种用法在现代C++中逐渐减少,仅在特定场景下使用:

  • 场景1:大型vector对象,避免栈溢出
    栈空间有限(通常几MB),如果要创建存储百万级intvector<int>,直接在栈上声明(vector<int> v(1000000);)会导致栈溢出 ,此时可以用vector<int>* pv = new vector<int>(1000000);(将vector对象放在堆上)。
  • 场景2:动态控制vector的生命周期
    比如需要让vector对象的生命周期跨越函数调用(如作为全局指针,在函数中创建,程序结束时销毁),或根据条件动态创建/销毁vector
  • 场景3:旧代码中作为函数参数传递(现代C++不推荐)
    早期C++中,为了避免函数参数传递vector时的拷贝开销,会用vector<int>*传递指针;但现代C++推荐用引用(vector<int>& const引用(const vector<int>&,效率更高且更安全。

注意:vector<int>*的裸指针容易出现忘记delete(内存泄漏)或悬垂指针(访问崩溃),现代C++推荐用unique_ptr<vector<int>>(独占所有权)或shared_ptr<vector<int>>(共享所有权)代替。

3. 核心区别总结
维度 vector<int*> vector<int>*
类型本质 vector容器(存储int*指针) 指针(指向vector<int>容器)
存储的核心内容 多个int*指针(地址) 一个vector<int>容器的地址
内存泄漏风险点 裸指针指向的堆内存未delete 指向的vector<int>对象未delete
现代C++替代方案 vector<unique_ptr<int>>/shared_ptr<int> unique_ptr<vector<int>>/shared_ptr<vector<int>>
常用程度 偶尔用(需指针场景) 极少用(现代C++推荐引用/移动语义)

三、补充示例代码

1. vector<int*> 的内存管理示例(裸指针版,需手动delete)
cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

int main() {
    vector<int*> v1;
    // 向v1中添加堆内存的int指针
    v1.push_back(new int(1));
    v1.push_back(new int(2));

    // 拷贝前:遍历delete原有元素(避免内存泄漏)
    for (int* p : v1) {
        delete p; // 释放指针指向的堆内存
    }
    v1.clear(); // 清空vector中的指针(可选,赋值时会自动销毁原有元素)

    // 赋值新的vector<int*>
    vector<int*> v2;
    v2.push_back(new int(5));
    v1 = v2;

    // 程序结束前:再次遍历delete v1中的元素
    for (int* p : v1) {
        delete p;
    }

    return 0;
}
2. vector<int>* 的示例(现代C++推荐用unique_ptr)
cpp 复制代码
#include <iostream>
#include <vector>
#include <memory> // 智能指针头文件
using namespace std;

int main() {
    // 旧写法:裸指针,需手动delete
    vector<int>* pv1 = new vector<int>{1,2,3};
    cout << pv1->size() << endl; // 访问vector的成员需用->
    delete pv1; // 必须delete,否则内存泄漏

    // 现代C++写法:unique_ptr,自动释放
    unique_ptr<vector<int>> pv2 = make_unique<vector<int>>({4,5,6});
    cout << pv2->size() << endl;
    // 无需delete,unique_ptr析构时自动释放vector对象

    return 0;
}
相关推荐
fpcc1 天前
跟我学C++中级篇——对类const关键字的分析说明
c++
寻星探路1 天前
【Python 全栈测开之路】Python 进阶:库的使用与第三方生态(标准库+Pip+实战)
java·开发语言·c++·python·ai·c#·pip
微露清风1 天前
系统性学习C++-第十八讲-封装红黑树实现myset与mymap
java·c++·学习
CSARImage1 天前
C++读取exe程序标准输出
c++
一只小bit1 天前
Qt 常用控件详解:按钮类 / 显示类 / 输入类属性、信号与实战示例
前端·c++·qt·gui
一条大祥脚1 天前
26.1.9 轮廓线dp 状压最短路 构造
数据结构·c++·算法
项目題供诗1 天前
C语言基础(一)
c++
@areok@1 天前
C++opencv图片(mat)传入C#bitmap图片
开发语言·c++·opencv
鸽芷咕1 天前
【2025年度总结】时光知味,三载同行:落笔皆是沉淀,前行自有光芒
linux·c++·人工智能·2025年度总结