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字节内存里

而在创建基本类型或结构体数组时,不会多分配内存

posted @ 2022-02-06 19:51  乘舟凉  阅读(310)  评论(0编辑  收藏  举报