delete与delete []的区别
一直很好奇delete 和 delete []有什么不同?今天我从汇编的角度看一看
测试源代码
#include <iostream>
int main()
{
char* data1 = new char[10];
char* data2 = new char[10];
std::cout << data1 << data2 << std::endl;
delete[] data1;
delete data2;
return 0;
}
使用vs2019查看其内部实现
delete
delete []
可以看到两个内部实现是一样的,说明delete 和 delete [] 使用没有区别
可是在网上查询资料说,当析构对象数组时必须要用delete [] ,如果使用delete的话会只调用数组第一个对象的析构函数。下面用vs看看
测试源代码
#include <iostream>;
using namespace std;
class T {
public:
T() { cout << "constructor" << endl; }
~T() { cout << "destructor" << endl; }
};
int main()
{
const int NUM = 3;
T* p1 = new T[NUM];
cout << hex << p1 << endl; //输出P1的地址
//delete[] p1;
delete p1;
cout << endl;
T* p2 = new T[NUM];
cout << p2 << endl; //输出P2的地址
delete[] p2;
return 0;
}
使用vs2019查看其内部实现
delete
ConsoleApplication1.exe!T::`scalar deleting destructor'(unsigned int):
00032590 push ebp
00032591 mov ebp,esp
00032593 sub esp,0CCh
00032599 push ebx
0003259A push esi
0003259B push edi
0003259C push ecx
0003259D lea edi,[ebp-0Ch]
000325A0 mov ecx,3
000325A5 mov eax,0CCCCCCCCh
000325AA rep stos dword ptr es:[edi]
000325AC pop ecx
000325AD mov dword ptr [this],ecx
000325B0 mov ecx,dword ptr [this]
000325B3 call T::~T (031514h)
000325B8 mov eax,dword ptr [ebp+8]
000325BB and eax,1
000325BE je __$EncStackInitStart+31h (0325CEh)
000325C0 push 1
000325C2 mov eax,dword ptr [this]
000325C5 push eax
000325C6 call operator delete (0310A0h)
000325CB add esp,8
000325CE mov eax,dword ptr [this]
000325D1 pop edi
000325D2 pop esi
000325D3 pop ebx
000325D4 add esp,0CCh
000325DA cmp ebp,esp
000325DC call __RTC_CheckEsp (0312F3h)
000325E1 mov esp,ebp
000325E3 pop ebp
000325E4 ret 4
可以看到000325B3处的call T::~T (031514h) 调用了一次析构函数
delete []
可以看到上面00CF26B7处将析构函数的地址作为第4个参数放入栈中,然后调用了00CF26C9处的eh vector destructor iterator函数,下面看看其内部实现
上面是一个典型的while循环,
00CF2EF6 mov ecx,dword ptr [destructor]
00CF2EF9 mov dword ptr [ebp-28h],ecx
00CF2EFC mov edx,dword ptr [ebp-28h]
00CF2EFF mov dword ptr [ebp-20h],edx
将析构函数放入ebp-20处,00CF2F0E call dword ptr [ebp-20h] 调用了析构函数。看来delete[] 是在释放内存前调用了对象数组的所有析构函数。
总结:
当释放基本数据类型数组内存时,delete和delete[] 是一样的,而当释放对象数组内存时,delete只会调用首地址对象的析构函数,而delete [] 是在释放内存前调用了对象数组的所有析构函数。
补充:
动态创建的对象数组是如何存储数组个数的
example:
#include <iostream>;
using namespace std;
class T {
public:
T() { cout << "constructor" << endl; }
~T() { cout << "destructor" << endl; }
};
int main()
{
int NUM = 3;
scanf_s("%d", &NUM);
T* p2 = new T[NUM];
cout << p2 << endl; //输出P2的地址
delete[] p2;
return 0;
}
输入5,结果
5
constructor
constructor
constructor
constructor
constructor
00B680BC
destructor
destructor
destructor
destructor
destructor
结果是对的,可是编译器是如何知道对象数组的个数,并且调用析构函数的呢
由上图可知,实际上编译器在分配内存会在首地址多分配4个字节,并且把数组的个数放入这多出来的4字节内存里
而在创建基本类型或结构体数组时,不会多分配内存