关于存储指针变量的vector的若干问题
前言
很多初学者认为学习C++很难的一个重要原因就是关于内存管理的问题,因为它不像Java等一些高级编程语言提供了垃圾自动回收的机制(你只管使用,剩下的交给OS);在C++中,如果你手动申请了一段内存空间,那么一定要记得在用完之后手动释放(你既要管申请,又要管释放),否则会造成内存泄漏等问题。这是一个让人又爱又恨的特性,你可以随意申请空间,但是又要小心翼翼地使用。那么说到内存管理,不得不涉及到C++中一个重要的角色——指针,指针的合理使用本身就是一个很难的问题,如果和vector等一些容器结合起来使用,出现的问题会更多,这里我列举几个在项目中遇到的问题,为了简化问题,以内置类型代替了复杂的类类型,目的只是为了还原一个场景。
一、插入后再delete
将一个指针变量插入vector之后,再手动delete,那么vector中的元素所指向的内存空间同样会失效,请看下面代码。
#include <iostream>
#include <vector>
using namespace std;
vector<int*> _data;
void Function()
{
for (int i = 0; i < 10; ++i)
{
int* temp1 = new int(i + 1);
_data.push_back(temp1);
delete temp1;
temp1 = nullptr; // 指针在delete之后一定要设为空,否则变成dangling pointer悬挂指针
}
}
int main()
{
Function();
for (int i = 0; i < _data.size(); ++i)
cout << *_data[i] << endl;
system("pause");
return 0;
}
可以看到,先将指针变量插入到vector中,然后再手动delete,那么vector中的元素所指向的内存空间会失效,但这却是一个非常良好的编程习惯,有new就必然有delete,不会造成内存泄漏。不过,这与我的初衷有悖,我想把1~10存入vector中,那么如何既保证功能又不会造成内存泄漏呢?
二、vector中指针变量的销毁
vector等一些容器在使用之后会自动调用析构函数,释放其占用的内存,那么我们是不是就不用管了,让析构函数完成释放内存的任务,请看下面代码。
int main()
{
vector<int*> data;
for (int i = 0; i < 10; ++i)
{
int* temp = new int(i + 1);
data.push_back(temp);
}
cout << "data的size:" << data.size() << endl;
cout << "data的capacity:" << data.capacity() << endl << endl;
int* element0 = data[0];
int* element1 = data[1];
int* element2 = data[2]; // 保留前三个指针变量
vector<int*> new_data;
new_data.swap(data); // 与空的vector进行交换,释放原始vector内存
cout << "data的size:" << data.size() << endl;
cout << "data的capacity:" << data.capacity() << endl << endl;
cout << "第1个元素:" << *element0 << endl;
cout << "第2个元素:" << *element1 << endl;
cout << "第3个元素:" << *element2 << endl;
system("pause");
return 0;
}
可以看到,通过与空的vector进行交换(data与new_data交换),可以达到释放内存的效果,但是奇怪的是,data中原来存储的前3个指针变量所指向的内存空间依然存储有效值,这是为什么呢?究其原因,data在释放内存的时候并不会自动delete其中的指针变量,所以指针变量依然存储有效值,要想使得指针变量也被销毁,必须手动delete。所以在第一个例子中,要想达到保留数据的同时又不会造成内存泄漏的问题,必须在vector使用完之后,循环遍历其中的指针变量,将其手动delete,但是vector的内存释放不需要我们操心,它是通过自动调用析构函数来实现的。
三、结论
1、指针变量插入vector中,再手动delete,指针变量所指向的内存空间同样会失效。
2、vector在调用析构函数释放其内存的时候,并不会自动delete其中的指针变量,我们必须通过循环遍历的方式手动delete。但是对于存储类对象的vector,其中的对象会自动调用析构函数,这点与存储指针变量有很大的不同。