delete与delete[]以及堆内存分配的一点说明
1. 简述
本文主要说明两点,第一,对于基本数据类型,delete和delete[]在释放数组上是一样的,至少结果上看是一样的。第二,堆内存地址分配大多数情况是无法预测的,而栈是更容易预测。
2. delete与delete[]
using namespace std;
class AK {
public:
AK() {
cout << "make one ak" << endl;
}
~AK() {
cout << "destroy one ak" << endl;
}
};
int main() {
int *p = new int[10];
cout << (size_t)p << endl; // 输出4013520
delete p;
p = new int[10];
cout << (size_t)p << endl; // 输出4013520
delete[] p;
p = 0;
//-------------------------
AK *pAK = new AK[10]; // 输出10次“make one ak”
cout << (size_t)pAK << endl; // 输出4013572
cout << (size_t)pAK << endl; // 输出4013596
delete []pAK; // 输出10次“destroy one ak”
system("PAUSE");
return 0;
}
指针p存储在栈中,开辟的空间在堆中,第一次,开辟的空间地址为4013520,用delete释放数组后,第二次,重新开辟,空间地址还是4013520。这说明,delete对于int数组的释放是同样有效的。
指针pAK存储在栈中,开辟的空间在堆中,第一次开辟的空间地址为 4013572,用delete释放数组后,第二次,重新开辟,空间地址为4013596。说明原来的空间没有完全释放掉,原因就是第一次用delete只调用了一次析构函数,即输出了1次“destroy one ak”。
3. 堆地址的预测难度很大
using namespace std;
int main() {
int*a = new int;
int*b = new int;
cout << (size_t)a << endl; // 输出4013416
cout << (size_t)b << endl; // 输出4013520
system("PAUSE");
return 0;
}
a,b两个int*存储在栈上,开辟两个int存储在堆中,堆中两个int的地址竟然相差了104个字节。一般如果是栈的话,两个连续定义的int,其地址肯定是连续的。
原因是堆的实现结构是链表,每次都从链表头开始扫描,找到能够满足需要的内存块就返回,剩余的小块继续放回链表,当有内存释放,再融入链表,因此堆是存在碎片的,而栈是顺序的存储结构,不存在碎片这一说,因此堆中的地址是很难预测的。堆的链表是管理结构,对于动态开辟内存十分适合,不过这也是其地址不好预测的原因之一。一种情况例外,即开辟几个对象,然后把这几个对象都释放掉,再按照相同的顺序开辟相同的对象,对应的地址是相同的。比如:分别开辟int,char,int[10],然后把他们都释放掉,再顺序的开辟int, char, int[10],对应的地址肯定是相同的,因为释放掉以后,链表恢复了原来的情况。
4. 总结
· 对于基本数据类型数组delete与delete[]一样,对于子定义的类型数组,只有delete[]可以。
· 堆是一种链表式存储结构,大多数情况下,使用其开辟的数据无法预测。
5. 感慨
delete,new,delete[],new[]这些操作符,不知道它们究竟是如何具体实现的,因此,很难真的理解为什么delete对基本数据类型数组也是可行,对自定义对象数组是不可行。不过呢,网上的文章看了很多,也没发现有讲出来的,也不纠结了,毕竟,C++很多都是人家隐藏好了,就是为了让你省事,如果不开发驱动级别或者系统级别程序的话,一般还真用不上。
本文介绍的这些都有一个特点,就是知道这些和不知道这些,对使用没什么大的区别,该new与delete对应还是对应,也没人没事找堆中开辟空间的地址,不过以后再看C++代码的时候,距离真相可能更近了一步,仅此而已。
6. 参考
delete与delete[]的区别 http://hi.baidu.com/shijg/blog/item/f1b24d16d4b4774920a4e987.html
C++中delete与delete[]的区别 http://jazka.blog.51cto.com/809003/230220