关于delete 和 delete[] (一)
岳迪
2023-12-01
1. 按C++标准的说法,对于非数组类型,分配函数是operator new,释放函数是operator delete。对于数组类型,分配函数operator new[],释放函数是operator delete[]。
If the allocated type is a non-array type, the allocation function’s name is operator new and the deallocation function’s name is operator delete. If the allocated type is an array type, the allocation function’s name is operator new[] and the
deallocation function’s name is operator delete[].
new操作符的作用是先调用分配函数分配足够的内存以便容纳所需类型的对象,再调用构造函数初始化内存中的对象。 delelte操作符相反,是先调用析构函数,再调用释放函数释放内存。注意这里 new operator和operator new的区别,一个是new操作符,一个是分配函数。
2. 当在应该使用delete[]的地方使用了delete, 在delete的地方使用了delelte[],不仅仅会造成内存泄漏,有可能会造成程序错误,先看一个例子:
为了了解new, new[], delete, delete[]做了些什么,我们先重载分配函数和释放函数。只是一个简单实现,如果按C++标准还要抛出bad_alloc异常什么的。
void* operator new (size_t size)
{
void *p=malloc(size);
printf("Adress after malloc: %p; size: %u/n",p,size);
return p;
}
void* operator new[] (size_t size)
{
void *p=malloc(size);
printf("Adress after malloc: %p; size: %u/n",p,size);
return p;
}
void operator delete[] (void* p)
{
printf("Adress before free: %p/n",p);
free(p);
}
void operator delete (void *p)
{
printf("Adress before free: %p/n",p);
free(p);
}
2.1 先来看看应该使用delete[]的地方使用了delete的情况
class A
{
public:
A() { printf("A::A()/n");}
~A() { printf("A::~A()/n");}
};
在这个例子中,本来应该用delelte[]的,而我们错误的使用了delete.
int main()
{
A *a = new A[1];
printf("Adress after new[] = %p/n", a);
delete a;
return 0;
}
运行结果:
Adress after malloc: 0x804a008; size: 5 //分配内存
A::A() //调用构造函数
Adress after new[] = 0x804a00c
A::~A() //调用析构函数
Adress before free: 0x804a00c //释放内存
*** glibc detected *** ./test2: free(): invalid pointer: 0x0804a00c ***
从结果中,我们可以看出,当new A[1]执行的时候,会先调用operator new[],而operator new[]函数又调用了malloc,注意,new A[1]返回的地址(0x804a00c)不是malloc返回的地址(0x804a008),而是+4返回,隐匿了4个字节(这里堆从低地址向高地址增加)。而这个被修改的地址传给了delete,而delete直接又把这个地址传给了free. 因为malloc得到的地址和free释放的地址,不一样,程序也就出错了。
另外,从malloc调用时的size看,也比下面的例子多了4个字节(size = 5 vs. size = 1),这个被隐匿起来的4字节干什么用的,我们后面在说。
2.2 再来看该用delete,却使用了delete[]的情况。
int main()
{
A *a = new A;
printf("Adress after new = %p/n", a);
delete[] a;
return 0;
}
运行结果:
Adress after malloc: 0x804a008; size: 1
A::A()
Adress after new = 0x804a008
A::~A()
...
Adress before free: 0x804a004
*** glibc detected *** ./test2: free(): invalid pointer: 0x0804a004 ***
这次malloc和new返回的地址是一样的了(0x804a008),问题却出在free[],free[]得到的地址是0x804a008,但它在调用operator delete[]的时候却自己把这个地址-4,变成了0x804a004。当free执行的时候,同样因为不是malloc分配的,从而程序出错。
下次我们再说这隐藏的四个字节用来干什么了,以及内置类型的delete和delete[]有什么区别。