C++中的new和delete


 

1. operator new 、placement new 和 operator delete

我们首先来看一下下面的简单示例

上面的代码很简单,
在代码中我们 new 了一个对象 p
然后调用函数 p->print()
最后将对象删除, delete p
上面的程序运行结果是这样的:

执行构造函数 MTestClass(int num)
Number is : 50
执行析构函数

我们可以对这个过程做出如下猜测的操作步骤:

  1. 在堆中使用 malloc 函数创建 sizeof(MTestClass) 大小的内存,并将内存转为 MTestClass* 类型;(使用 operator new
  2. 调用构造函数 MTestClass(50) 并将已分配的内存设置值。(使用placement new)
  3. 调用函数 p->print();
  4. 调用析构函数 ~MTestClass()
  5. 释放申请的堆空间内存。(operator delete)
  • operator new : 是指形如以下形式 void* operator new (size_t size) 形式的函数,分配 size 大小的内存空间。
  • placement new : 调用构造函数,并为已将分配的内存空间赋值,这块内存可以是栈内存也可以是堆内存。
    形如 ::new(obj) MTestClass(50),为指针 obj 指向的已存在的内存空间赋值
  • operator delete : 释放堆内存空间。

我们改写上面的示例:

程序的运行结果如下:
执行 operator new !
Number is : -842150451
执行构造函数 MTestClass(int num)
Number is : 50
执行析构函数
执行 operator delete !

根据运行结果可以看出,

  1. 首先执行 operator newoperator new 虽然分配了内存空间,但是并没有为这块内存空间赋值;
  2. 执行 placement new,调用构造函数并为这块空间赋值。
  3. 执行析构函数
  4. 执行 operator delete 释放内存。
 

2. new[] 和delete[]

我们经常说 new[]delete[] 一定要成对出现,那么为什么呢?
其实是这样的,对于普通数据类型使用 new[] 创建的数组,使用 delete[] 和直接使用 delete 释放内存效果是一样的,对于自定义的类型,如果显示的声明了析构函数,则需要使用 delete[], 如果不是显示的声明析构函数,那么 deletedelete[] 的效果是相同的。
如果显示声明了析构函数,delete[] 需要调用n次析构函数,那么 delete[] 是怎么知道申请了多少个内存空间呢?我们可以看一下下面的伪代码

分析如下:
首先 new MTestClass[10] 会申请 10 sizeof(MTestClass) + sizeof(size_t) 个内存空间,为什么是 10 sizeof(MTestClass) + sizeof(size_t) 个而不是 *10 sizeof(MTestClass) 个,是因为他要用 sizeof(size_t) 个字节存储数组的元素个数。
在调用
delete[]** 的时候会根据第一个字节的个数去调用n次析构函数,然后释放掉整个内存空间。

为了验证上面的说法,我们将代码改写如下:

运行结果为:
8, 4
执行 operator new[] ! size is 44
执行构造函数 MTestClass()
执行构造函数 MTestClass()
执行构造函数 MTestClass()
执行构造函数 MTestClass()
执行构造函数 MTestClass()
执行析构函数
执行析构函数
执行析构函数
执行析构函数
执行析构函数
执行 operator delete[] !

可以看出,operator new[] 申请的内存空间大小为44个字节,sizeof(MTestClass) 为8字节,而 sizeof(size_t) 为4字节,正好为 44 = 5 8 + 4,验证了申请空间大小为 n sizeof(MTestClass) + sizeof(size_t)

如果我们不显示声明析构函数,又会怎么样呢??
去掉析构函数的完整代码如下:

程序运行结果:
8, 4
执行 operator new[] ! size is 40
执行构造函数 MTestClass()
执行构造函数 MTestClass()
执行构造函数 MTestClass()
执行构造函数 MTestClass()
执行构造函数 MTestClass()
执行 operator delete[] !

从结果可以看出,申请的大小为40,验证了如果没有显示的声明析构函数,则会申请 sizeof(MTestClass) * n 个字节的内存空间,这时使用delete 和使用 delete[] 效果是相同的。

通过上面这两个例子也可以看出
operator newoperator new[] 的效果是相同的,都是申请 size 大小的空间;
operator deleteoperator delete[] 的效果也是相同的,都是释放堆内存空间。

You May Also Like

About the Author: admin

喜欢编程、爱游戏,更爱生活。

发表评论

电子邮件地址不会被公开。