指针与删除
删除指向指针数组的指针
有以下代码:
int **p;
p = new int*[5];
表示p为一个指向int型指针的指针,指向了一个int型数组的首地址,该数组中存放的int型指针
我们如何释放p对应的空间呢?
for(int i = 0; i < 5; i++)
{
if(p[i] != NULL)
{
delete p[i]; // 删除数组中的int型指针p[i]
p[i] = NULL; // 同时将该指针指向空
}
delete[] p; // 删除数组指针p
}
野指针
以下代码:
int *p = new int(5); // 表示一直指针p,指向一个空间,该空间存放一个5
如果我们delete
他
delete p;
那么结果是,该空间中的内容被删除,即该空间被释放,但p依旧指向该空间,此时如果再次调用
delete p;
那么就会报错,因为p成为了野指针,就是,指向一个空间,但该空间中什么也没有。
并且C++编译器也没法识别这个指针是否为野指针,我们也没法让程序识别。
一般情况下不会有问题,但如果我们想进行以下操作:
class A
{
public:
int **p; // p为指向int型指针的指针
p = new int*[5]; // 此时p为一个指针,指向一个地址,该地址中存放的为一个数组的首地址,这个数组长度为5,且里面的元素为int型指针。
A()
{
for(int i = 0; i < 5;i++)
p[i] = NULL; // 此时p[i] 这个指针就不再是野指针,而成为了一个指向NULL的指针
}
~A()
{
// 析构函数
}
};
// 我们进行赋值
for(int i = 0; i < 5; i++)
p[i] = new int(i); // 此时,*p[i] = i
// 我们为了某个目的,需要将p[2] 的空间释放,也就是执行 delete p[2]的操作:
delete p[2]; // 此时p[2] 仍然指向原来的空间,但原来的空间被释放,且里面什么也没有
此时如果我们需要对上述类的内容进行析构
我们这么编写析构函数:
~A()
{
for(int i = 0; i < 5; i++)
delete p[i]; // 释放数组中的指针
delete[] p; // 释放指向数组的指针
}
释放过程是没有问题的,但是,因为我们之前执行过这个操作:
delete p[2];
这个操作已经对\(p[2]\) 中的地址进行了释放,此时析构函数就对同一块地址进行了重复释放,就会导致内存错误
如果我们对析构函数进行以下更改:
~A()
{
for(int i = 0 ; i < 5; i++)
{
if(p[i] == NULL)
continue; // 释放过的就跳过
delete p[i];
}
delete[] p;
}
但问题又来了,我们当时释放\(p[2]\) 的时候,只是delete
了一下,没有将p[2]
指向\(NULL\),所以析构函数中的\(if\)语句仍然无法识别我们的意图,仍然对同一块地址重复释放。
所以,我们必须养成良好的习惯:
当我们释放某个指针\(p\)对应的地址的时候,我们不仅要
delete p; // 释放p指向的地址
还要将\(p\)指向\(NULL\)
也就是完整的释放过程应该为以下两条语句:
delete p;
p = NULL;