深入剖析delete 和delete[]

 

发信人: pthread (ZZ), 信区: C_Cpp

标 题: 关于delete delete[]

发信站: 兵马俑BBS (Sat Sep 13 12:59:07 2008), 本站(bbs.xjtu.edu.cn)

 

1. C++标准的说法,对于非数组类型,分配函数是operator new,释放函数是operator delete。对于数组类型,分配函数operator new[],释放函数是operator delete[]

 

If the allocated type is a non-array type, the allocation functions name is operator new and the deallocation functions name is operator delete. If the allocated type is an array type, the allocation functions name is operator new[] and the

deallocation functions name is operator delete[].

 

new操作符的作用是先调用分配函数分配足够的内存以便容纳所需类型的对象,再调用构造函数初始化内存中的对象。delelte操作符相反,是先调用析构函数,再调用释放函数释放内存。注意这里new operatoroperator 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: 0x 804a 008; size: 5 //分配内存

A::A()                                  //调用构造函数

Adress after new[] = 0x 804a 00c           

A::~A()                                 //调用析构函数

Adress before free: 0x 804a 00c            //释放内存

*** glibc detected *** ./test2: free(): invalid pointer: 0x 0804a 00c ***

 

从结果中,我们可以看出,当new A[1]执行的时候,会先调用operator new[],operator new[]函数又调用了malloc,注意,new A[1]返回的地址(0x 804a 00c )不是malloc返回的地址(0x 804a 008),而是+4返回,隐匿了个字节(这里堆从低地址向高地址增加)。而这个被修改的地址传给了delete,delete直接又把这个地址传给了free. 因为malloc得到的地址和free释放的地址,不一样,程序也就出错了。

另外,从malloc调用时的size看,也比下面的例子多了个字节(size = 5 vs. size = 1),这个被隐匿起来的字节干什么用的,我们后面在说。

 

2.2 再来看该用delete,却使用了delete[]的情况。

int   main()

{

    A *a   =   new A;

    printf("Adress after new = %p/n", a);

    delete[] a;

    return 0;

}

 

运行结果:

Adress after malloc: 0x 804a 008; size: 1

A::A()

Adress after new = 0x 804a 008

A::~A()

...

Adress before free: 0x 804a 004

*** glibc detected *** ./test2: free(): invalid pointer: 0x 0804a 004 ***

 

这次mallocnew返回的地址是一样的了(0x 804a 008),问题却出在free[]free[]得到的地址是x 804a 008,但它在调用operator delete[]的时候却自己把这个地址-4,变成了x 804a 004。当free执行的时候,同样因为不是malloc分配的,从而程序出错。

 

3. 下面我们来看看上面提到的字节是干什么用的。

int   main()

{

    A *a   =   new   A[100];

    printf("Adress after new = %p/n", a);

    printf("number of A at %p  = %d/n", (int*)a-1, *((int*)a-1));

    printf("size of a at %p = %d/n", (int*)a-2, *((int*)a-2));

    delete[] a;

    return 0;

}

 

运行结果:

Adress after malloc: 0x 804a 008; size: 104

A::A()

...

Adress after new = 0x 804a 00c

number of A at 0x 804a 008  = 100

size of a at 0x 804a 004 = 113

A::~A()

...

Adress before free: 0x 804a 008

 

从上面的运行结果可以看出,那个被隐藏起来的四字节放的是数组元素的个数(number of A at 0x 804a 008  = 100)。当使用new[]的时候,new[]会多分配个字节,用来存放数组的个数,然后把malloc返回的地址+4给用户使用。而在free[]的时候,free[]会把得到的地址-4,得到内存中对象的个数,然后把这个地址交于free释放。如果我们把这个值修改了如*((int*)a-1) = 50。我们就会发现,上面的程序调用了次构造函数,但是只调用了次析构函数,因为delete[]以为只有个数组元素。

其实malloc自身也隐匿了一些内存空间用于自身管理,如malloc返回的地址-4,可以看到malloc分配的空间大小。如果我们把这个值修改了,如:*((int*)a-2) = 0, 程序也会出错。关于malloc这里就不多说了,可以看看相关的资料或者Linux版上我写的帖子。

 

4. 关于内置类型和没有析构函数的类类型,无论是delete, delete[]都不会出错。

class   A

{

    public:

        A() { printf("A::A()/n");}

//         ~A() { printf("A::~A()/n");}

};

 

int   main()

{

    A   *a   =   new   A[1];

    printf("Adress after new[] = %p/n", a);

    delete   a;

    return 0;

}

 

运行结果:

Adress after malloc: 0x 804a 008; size: 1

A::A()

Adress after new = 0x 804a 008

Adress before free: 0x 804a 008

 

可以看出new []返回的地址就是malloc的地址,同理,free[]传递给free的地址也一样。所以free的时候不会有问题。编译器在遇到没有析构函数的类的时候,就认为对象中没有需要额外释放的内存和处理的数据。所以在既不影响程序正确执行有没有内存泄漏的情况下,直接释放对象所占用的内存就好了。

注意:这完全是编译器优化的结果,并不能保证程序在所有环境下都能正确执行。

 

以上所有程序用gcc (GCC) 4.1.0 编译

 

小结:

1. 无论什么类型,用new[]分配的用delet[]释放,new分配的用delete释放。

2. new[]delete[]的时候会使用隐藏的内存来记录一些数组信息,如数组元素个数。这造成了错误的使用了deletedelete[]的时候,会造成程序错误。

3. 对于内置类型和没有析构函数的类类型,编译器可能对其进行了优化,造成deletedelete[]都不会出错,或者内存泄漏。但这是不符合标准,且依赖于具体实现的。

 

Pthread 08.09.13

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值