多少年了,一直处于C与C++混用的状态,申请空间一直用malloc,释放空间一直用free,为什么?因为他们好理解易操作,就如同输出一直用printf而不用<<,输入一直用scanf而不用>>。如果用new和delete,就会涉及到数组的问题,而我却一直没有好好理解过这种情况。今天突然有兴致深入分析下,于是写了下面一段代码:
代码
编译运行之后可以看到:
反汇编之后可以看出,程序首先申请了4字节的空间,然后调用构造方法进行初始化,接着便把地址赋给了pTestClass,最后调用析构方法收尾并把空间释放掉:
代码
再来看看申请对象数组的情况,将main函数中的代码修改为:
编译运行之后可以看到:
反汇编之后可以看出,程序首先申请了20字节空间,然后将数组大小存放到前4个字节,调用构造方法将后面的4个对象指针初始化(4*4+4=20B),接下来才把空间指针赋给pTestClass。在清除空间的时候,程序根据存储在前4个字节中的长度,通过一个循环来从后往前调用每个对象的析构方法,最后才把空间释放掉:
代码
那么如果将main函数中的代码修改为申请一个对象的数组,情况又如何呢?
反汇编后可以看到,由于是简单数据类型,不需要调用构造方法和析构方法,在申请空间时,没有多申请4个字节的空间来保存数组大小,所以,调用delete进行删除和调用delete []进行删除是完全一样的。

#include <iostream>
using namespace std;
int g_i = 0;
class TestClass
{
public:
int id;
TestClass()
{
id = ++g_i;
cout << id << " TestClass created." << endl;
}
~TestClass()
{
cout << id << " TestClass deleted." << endl;
}
};
int main(int argc, char* argv[])
{
TestClass * pTestClass = new TestClass;
delete pTestClass;
return 0;
}
using namespace std;
int g_i = 0;
class TestClass
{
public:
int id;
TestClass()
{
id = ++g_i;
cout << id << " TestClass created." << endl;
}
~TestClass()
{
cout << id << " TestClass deleted." << endl;
}
};
int main(int argc, char* argv[])
{
TestClass * pTestClass = new TestClass;
delete pTestClass;
return 0;
}
编译运行之后可以看到:
1 TestClass created.
1 TestClass deleted.
1 TestClass deleted.
反汇编之后可以看出,程序首先申请了4字节的空间,然后调用构造方法进行初始化,接着便把地址赋给了pTestClass,最后调用析构方法收尾并把空间释放掉:

push 4
call operator_new
......
mov ecx, [ebp+var_18]
call TestClass__TestClass
mov [ebp+var_24], eax
......
push 1
mov ecx, [ebp+var_1C]
call TestClass___scalar_deleting_destructor
call operator_new
......
mov ecx, [ebp+var_18]
call TestClass__TestClass
mov [ebp+var_24], eax
......
push 1
mov ecx, [ebp+var_1C]
call TestClass___scalar_deleting_destructor
再来看看申请对象数组的情况,将main函数中的代码修改为:
TestClass * pTestClass = new TestClass[4];
delete [] pTestClass;
delete [] pTestClass;
编译运行之后可以看到:
1 TestClass created.
2 TestClass created.
3 TestClass created.
4 TestClass created.
4 TestClass deleted.
3 TestClass deleted.
2 TestClass deleted.
1 TestClass deleted.
2 TestClass created.
3 TestClass created.
4 TestClass created.
4 TestClass deleted.
3 TestClass deleted.
2 TestClass deleted.
1 TestClass deleted.
反汇编之后可以看出,程序首先申请了20字节空间,然后将数组大小存放到前4个字节,调用构造方法将后面的4个对象指针初始化(4*4+4=20B),接下来才把空间指针赋给pTestClass。在清除空间的时候,程序根据存储在前4个字节中的长度,通过一个循环来从后往前调用每个对象的析构方法,最后才把空间释放掉:

push 14h
call operator_new
......
push offset TestClass___TestClass
push offset loc_40128F
mov eax, [ebp+var_18]
mov dword ptr [eax], 4
push 4
push 4
mov ecx, [ebp+var_18]
add ecx, 4
push ecx
call vector constructor iterator
......
push 3
mov ecx, [ebp+var_1C]
call TestClass___vector_deleting_destructor
call operator_new
......
push offset TestClass___TestClass
push offset loc_40128F
mov eax, [ebp+var_18]
mov dword ptr [eax], 4
push 4
push 4
mov ecx, [ebp+var_18]
add ecx, 4
push ecx
call vector constructor iterator
......
push 3
mov ecx, [ebp+var_1C]
call TestClass___vector_deleting_destructor
那么如果将main函数中的代码修改为申请一个对象的数组,情况又如何呢?
TestClass * pTestClass = new TestClass[1];
delete [] pTestClass;
delete [] pTestClass;
通过反汇编可以看出,即使是申请一个对象的指针数组,编译器的处理都是一样的,多申请4个字节的空间来保存数组大小。所以,只要在new操作时,使用了[],在delete时也必须使用[],否则,程序将把前面存贮数组大小的4个字节按照对象指针来处理,这样在调用析构方法的时候就会出错。
那么如果是在一个文件中进行new操作,在另一个文件中delete的时候,如何判断使用delete还是delete []呢?一种方法是用一个变量来保存新申请对象的个数,在delete的时候进行判断,如果个数大于1,使用delete[],否则使用delete;另一种方法是,在申请对象的时候,一律用上[],这样在delete的时候也一律用上[]就没问题。
最后我们来看看申请简单类型数组的情况,将main函数中代码改为:
int * pInt = new int[4];
delete [] pInt;
delete [] pInt;
反汇编后可以看到,由于是简单数据类型,不需要调用构造方法和析构方法,在申请空间时,没有多申请4个字节的空间来保存数组大小,所以,调用delete进行删除和调用delete []进行删除是完全一样的。
push 10h
call operator_new
......
push edx
call free_4062B0
call operator_new
......
push edx
call free_4062B0
今天才发现,在多线程下new和delete的时候,必须选择上多线程库,不然可能造成进程崩溃,具体设置位置在工程设置“C/C++”选项卡的“Code Generation”下的“Use run-time library”中。
http://www.cnblogs.com/God4/articles/1887184.html#2320993