指针与删除

删除指向指针数组的指针

有以下代码:

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;