提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
一、vector<int>* pv 的初始化方式
vector<int>* 是指向vector<int>的指针,其初始化核心是为指针赋予有效的内存地址(可以是空地址、栈上vector的地址、堆上vector的地址)。以下是常见初始化方式,按推荐度和实用性排序:
1. 空指针初始化(默认/显式)
指针未指向任何有效vector对象,初始化为空指针(C++11起推荐用nullptr,旧版用NULL)。
#include <vector>
using namespace std;
int main() {
// 方式1:默认初始化(裸指针默认是野指针,危险!建议显式初始化为空)
vector<int>* pv1; // 野指针:指向随机内存,禁止解引用/调用成员
// 方式2:显式初始化为空指针(推荐)
vector<int>* pv2 = nullptr; // C++11起
vector<int>* pv3 = NULL; // 旧版C++,本质是0
return 0;
}
注意:空指针不能解引用或调用vector的成员(如pv2->size()),否则会触发未定义行为(程序崩溃)。
2. 指向栈上的vector<int>对象(不推荐,风险高)
将指针指向栈上声明的vector<int>对象的地址(用&取地址)。
int main() {
vector<int> v = {1,2,3}; // v是栈上的vector对象
vector<int>* pv = &v; // pv指向栈上的v
// 此时pv可以正常使用,但存在致命风险:
// 如果v先被销毁(比如离开作用域),pv会变成**悬垂指针**(指向已释放的内存)
return 0;
}
风险:栈上的vector对象生命周期由编译器管理(离开作用域即销毁),若指针在对象销毁后仍被使用,会导致程序崩溃。仅临时使用且确保对象生命周期更长时,才偶尔用这种方式,不推荐在生产代码中使用。
3. 指向堆上的vector<int>对象(裸指针最常用的方式)
通过new关键字在堆上创建vector<int>对象,指针指向该对象的地址(这是裸指针vector<int>*的核心使用场景)。
int main() {
// 方式1:创建空的堆vector,后续添加元素
vector<int>* pv1 = new vector<int>();
pv1->push_back(1);
pv1->push_back(2);
// 方式2:创建时直接初始化元素
vector<int>* pv2 = new vector<int>{1,2,3,4,5};
// 方式3:创建时指定大小和初始值
vector<int>* pv3 = new vector<int>(10, 0); // 10个元素,每个都是0
// 必须手动delete,否则内存泄漏!
delete pv1;
delete pv2;
delete pv3;
return 0;
}
关键:堆上的vector对象生命周期由程序员管理,必须用delete销毁(否则会导致内存泄漏)。
4. 指向已有堆上vector<int>的指针(指针赋值)
将一个vector<int>*指针赋值给另一个,本质是让两个指针指向同一个堆上的vector对象(浅拷贝)。
int main() {
vector<int>* pv1 = new vector<int>{1,2,3};
vector<int>* pv2 = pv1; // pv2和pv1指向同一个堆vector
// 此时修改pv2,pv1也会受影响(因为指向同一个对象)
pv2->push_back(4);
cout << pv1->size() << endl; // 输出4
// 注意:只能delete一次!重复delete会触发未定义行为
delete pv1;
// delete pv2; // 错误:重复释放同一内存
return 0;
}
5. 现代C++:用智能指针替代裸指针(推荐)
裸指针vector<int>*存在忘记delete(内存泄漏)、重复delete、悬垂指针等风险,现代C++推荐用unique_ptr/shared_ptr(智能指针)管理堆上的vector,无需手动delete。
#include <vector>
#include <memory> // 智能指针头文件
using namespace std;
int main() {
// 方式1:unique_ptr(独占所有权,推荐)
unique_ptr<vector<int>> pv1 = make_unique<vector<int>>({1,2,3});
pv1->push_back(4);
// 方式2:shared_ptr(共享所有权,需多指针共享时用)
shared_ptr<vector<int>> pv2 = make_shared<vector<int>>(10, 0);
// 无需手动delete,智能指针析构时自动释放堆vector
return 0;
}
二、vector<int>* pv 的使用方法
使用指针访问vector的成员或元素,主要通过**->运算符**(指针直接访问)或解引用后用.运算符(先解引用指针得到vector对象,再访问)。
#include <iostream>
#include <vector>
#include <memory>
using namespace std;
int main() {
// 1. 裸指针使用
vector<int>* pv = new vector<int>{1,2,3,4,5};
// 访问vector的成员:-> 运算符(推荐)
cout << "size: " << pv->size() << endl; // 输出5
cout << "capacity: " << pv->capacity() << endl; // 输出5(多数实现)
// 访问vector的元素:两种方式
cout << "第一个元素:" << (*pv)[0] << endl; // 解引用后用[],输出1
cout << "第二个元素:" << pv->at(1) << endl; // 用->访问at()方法,输出2(带越界检查)
// 遍历元素
for (int num : *pv) { // 解引用指针,得到vector对象后遍历
cout << num << " "; // 输出1 2 3 4 5
}
cout << endl;
// 修改元素
(*pv)[0] = 10;
pv->at(1) = 20;
for (int num : *pv) {
cout << num << " "; // 输出10 20 3 4 5
}
cout << endl;
// 销毁裸指针
delete pv;
// 2. 智能指针使用(语法和裸指针一致,用->)
unique_ptr<vector<int>> pv2 = make_unique<vector<int>>({6,7,8});
cout << "pv2 size: " << pv2->size() << endl; // 输出3
return 0;
}
三、会不会直接使用vector<int>* pv(裸指针)?
结论:旧代码/特定场景会用,但现代C++中极少直接用裸指针,推荐用智能指针/替代方案。
1. 为什么会用vector<int>*(裸指针)?
- 场景1:避免栈溢出
栈空间有限(通常几MB),如果创建超大的vector对象(比如存储百万/千万级元素),直接在栈上声明vector<int> v(10000000);会导致栈溢出。此时用vector<int>* pv = new vector<int>(10000000);将vector对象放在堆上,可避免栈溢出。 - 场景2:动态控制
vector的生命周期
需要让vector对象的生命周期跨越函数调用(比如全局指针,在A函数中创建,B函数中使用,程序结束时销毁),或根据条件动态创建/销毁vector(比如用户触发某个事件时才创建)。 - 场景3:旧代码兼容
早期C++中,为了避免函数参数传递vector时的拷贝开销,会用vector<int>*传递指针(现代C++已被引用替代)。
2. 现代C++的替代方案(为什么不推荐裸指针)?
裸指针vector<int>*的问题:容易忘记delete(内存泄漏)、重复delete(程序崩溃)、出现悬垂指针(访问无效内存)。现代C++推荐:
- 替代方案1:智能指针(
unique_ptr<vector<int>>/shared_ptr<vector<int>>):完全替代裸指针,自动管理内存,无泄漏风险。 - 替代方案2:引用(
vector<int>&):函数参数传递时,用引用替代指针,效率高且更安全(不会出现空指针)。 - 替代方案3:移动语义:C++11起,
vector支持移动语义(std::move),传递大vector时用移动代替拷贝,效率和指针相当,且更安全。
四、pv指向堆空间的vector,是不是所有内存都在堆上?
结论:不是!要分层拆解内存分布,只有部分内存一定在堆上,指针变量本身可能在栈上。
C++的内存分布核心规则:局部变量(非static)在栈上,new/malloc分配的在堆上;且vector的底层元素数组永远在堆上(无论vector对象本身在栈还是堆)。
我们以**局部变量vector<int>* pv = new vector<int>{1,2,3};**为例,拆解三层内存分布:
| 层级 | 内容 | 存储位置 | 说明 |
|---|---|---|---|
| 第一层(指针本身) | pv(vector<int>*类型的指针变量) | 栈上(局部变量) | 指针变量本身占用8字节(64位系统),存储的是堆上vector对象的地址。 |
| 第二层(指向的对象) | vector<int>对象(堆上创建的) | 堆上 | new分配的vector对象本身(包含size、capacity、指向元素数组的指针等成员)。 |
| 第三层(vector内部) | vector的int元素数组(如{1,2,3}) | 堆上 | vector的底层动态数组,永远在堆上(vector的实现特性)。 |
例外情况:指针本身也在堆上(极少用)
如果通过new创建指针变量本身(vector<int>** ppv = new vector<int>*;),则:
- 第一层:
ppv(vector<int>**类型的指针变量)→ 栈上(局部变量)。 - 第二层:
*ppv(vector<int>*类型的指针)→ 堆上(new分配)。 - 第三层:
**ppv(vector<int>对象)→ 堆上(new分配)。 - 第四层:
vector的元素数组 → 堆上。
这种写法极度罕见,且会增加内存管理的复杂度,完全不推荐。
总结
vector<int>* pv的初始化:主要有空闲指针、指向栈上vector(不推荐)、指向堆上vector(裸指针常用)、智能指针(推荐)四种方式。- 使用方法:通过
->访问成员,解引用后访问元素,语法和普通vector一致。 - 使用场景:旧代码/避免栈溢出/动态生命周期会用裸指针,现代C++推荐智能指针/引用/移动语义。
- 内存分布:pv指向堆上vector时,指针变量本身(局部)在栈上,vector对象和其内部元素数组在堆上,并非所有内存都在堆上。
注
上面代码中
unique_ptr<vector<int>> pv1 = make_unique<vector<int>>({1, 2, 3});//会报错
//应换为
//显式构造vector临时对象(推荐,解决初始化列表推导问题)
unique_ptr<vector<int>> pv1 = make_unique<vector<int>>(vector<int>{1, 2, 3});
1054

被折叠的 条评论
为什么被折叠?



