今天想到了这个问题,开始以为和数组一样,要在同一个作用域内才能知道释放多少空间:
#include "iostream"
using namespace std;
class A{
int static i;
public:
A(){}
~A(){cout<<"释放第"<<++i<<"个对象"<<endl;};
};
int A::i = 0;
int main(){
A *f = new A[11];
delete[] f;
return 0;
}
这样会释放11个对象。
但是下面这样不在同一个作用域它也能释放掉11个对象:
#include "iostream"
using namespace std;
class A{
int static i;
public:
A(){}
~A(){cout<<"释放第"<<++i<<"个对象"<<endl;};
};
int A::i = 0;
A* test(){
return new A[11];
}
int main(){
A *f = test();
delete[] f;
return 0;
}
很好奇这个时候main函数是怎么知道要释放11个对象的空间的呢?因为test函数只是返回了一个指针,并没有返回申请空间大小的信息。去搜索了一下,原来会在申请的空间首位置之前的4个字节内记录申请了多少空间(可以理解为数组下标为-1的位置,但是这样理解并不总是正确):
#include "iostream"
using namespace std;
class A{
int static i;
public:
A(){}
~A(){cout<<"释放第"<<++i<<"个对象"<<endl;};
};
int A::i = 0;
A* test(){
return new A[11];
}
int main(){
A *f = test();
//获取申请空间头部之前的4字节地址
int addr = (int)f - 4;
//输出该4字节空间里面存储的内容
cout<<"即将释放"<<*(int *)addr<<"个对象:"<<endl;
delete []f;
return 0;
}
下面的程序做了3种情况的测试。同时对每个对象加了个ID,通过输出会发现释放的顺序是从数组尾端往前端释放,即先申请的后释放。
#include "iostream"
using namespace std;
class A{
int static i;
int static j;
public:
int myID;
A():myID(j++){}
~A(){cout<<"释放第"<<++i<<"个对象"<<";其标号为"<<myID<<endl;};
};
int A::i = 0;
int A::j = 0;
A* test(){
return new A[11];
}
int main(){
//测试一:
//只申请一个的情况,由于只释放一个,所以前四字节值无意义
A *f = new A;
//获取申请空间头部之前的4字节地址
int addr = (int)f - 4;
//输出该4字节空间里面存储的内容
cout<<"用new申请空间,前四字节值无意义:"<<*(int *)addr<<endl;
delete f;
cout<<endl;
//测试二:
//更改4字节空间里面的值为5,那么就只会释放5个对象
f = test();
//获取申请空间头部之前的4字节地址
addr = (int)f - 4;
//更改该4字节空间里面的值
*(int *)addr = 5;
//输出该4字节空间里面存储的内容
cout<<"即将释放"<<*(int *)addr<<"个对象:"<<endl;
delete []f;
cout<<endl;
//测试三:
//更改4字节空间里面的值为20,那么就会释放20个对象.后面内存按照A类型的内存布局进行解释
//(当然多余的空间要保证能读写,比如把20改为2000很可能就跑到其他程序的空间里面去了,这个时候程序就崩溃了)
f = test();
//获取申请空间头部之前的4字节地址
addr = (int)f - 4;
//更改该4字节空间里面的值
*(int *)addr = 20;
//输出该4字节空间里面存储的内容
cout<<"即将释放"<<*(int *)addr<<"个对象:"<<endl;
delete []f;
return 0;
}