C/C++编程:delete 表达式

1059 篇文章 285 订阅

作用

  • 销毁先前由new表达式分配的对象

语法

::(可选) delete 表达式(1)
::(可选) delete [] 表达式(2)
  1. 销毁 new 表达式创建的单个非数组对象
  2. 销毁 new[] 表达式创建的数组

对于第一种(非数组)形式:

  • 表达式 必须是如下两种情况之一:
  • 值 必须是如下三种情况之一:
    • 必须为空(null)
    • 或指向new表达式所创建的非数组对象的指针
    • 或指向 new 表达式所创建的对象的基类子对象的指针

若 表达式 为其他值,包括它是通过new 表达式的数组形式获得的指针的情况,其行为未定义。

对于第二种(数组)形式,

  • 表达式 必须是如下两种情况之一:
    • 空指针值
    • 先前由 new 表达式的数组形式所获得的指针值

若 表达式 为其他值,包括若它是由 new 表达式的非数组形式获得的指针的情况,其行为未定义。

表达式的结果始终具有 void 类型。

也就是说:调用new申请的内存用delete释放;调用new[]申请的内存就一定要用delete[]释放。

我们来看个例子:

  • 考虑有如下类定义:
class Test{
public:
    Test();
    ~Test();
};
  • 当我们在堆中创建一个新对象时,可以用如下代码
  Test* pTest = new Test;    
  • 当我们要释放这个对象时可以使用以下代码:
   delete pTest;
  • 当我们要在堆中创建10个对象是可以使用以下代码:
   Test* pTests = new Test[10];
  • 当我们要释放这10个对象时,与new[]相呼应,必须使用delete[]

两者的区别在于带[]new []delete[]会对每个元素调用构造函数和习惯函数

注解

不能删除指向void的指针,因为它不是指向完整对象类型的指针。

自c++11起,因为关键词 delete 之后的一对方括号始终被解释为 delete 的数组形式,所以紧跟在 delete 之后的拥有空俘获列表的 lambda 表达式必须以括号括起来。

// delete []{return new int; }(); // 解析错误
delete ([]{return new int; })(); // OK

问题

为什么在调用delete[]时,括号中不需要指定元素的个数

据说初期的C++在调用delete[]时是需要指定元素个数的,但是那样会非常麻烦。所以后来进行了改进:在实现new[]的时候同时申请内存空间保存元素的个数。基本上是这个样子:

struct array {
    size_t count_of_test;
    Test t[10];
};

在调用new[]时先通过malloc申请内存多申请一点空间保存count_of_test,然后是返回给用户t空间的例子。这个地址要在malloc获得的array地址上加一个偏移量。最后对这个t空间调用每个元素的构造函数。

调用delete[]的参数实际上是t空间的地址。先对这个地址参数进行减偏移量运算得到array地址并取得count_of_test,然后对t空间调用析构函数count_of_test次。最后以array地址作为参数调用free()函数。

除了正确调用构造函数/析构函数以外,还要处理保存元素个数的空间。

所以必须配对使用。

其他

为了避免内存泄漏,每个动态内存分配必须与一个等同相反的deallocation对应。函数operator delete与delete操作符的关系与operator new与new操作符的关系一样。当:

string *ps;
.....
delete ps;

operator delete调用:

void operator delete(void *memoryToBeDeallocated);

导致编译器生成(编译器会生成代码来析构对象并释放对象的内存。):

ps->~string();
operator delete(ps);

这又一个隐含的意思,如果你只想处理未被初始化的内存,你应该绕过new和delete操作符,而调用operator new获得内存和operator delete释放内存给系统:

void *buffer = operator new(50 * size(char)); //分配足够的内存以容纳50个char,没有调用构造函数
...
operator delete(buffer); //释放内存,没有调用析构函数

这与C中调用malloc和free等同。

如果你用placement new在内存中建立对象,你应该避免在该内存中用delete操作符。因为delete操作符调用operator delete来释放内存,但是包含对象的内存最初不是使用operator new分配的,placement new只是返回传递给它的指针。你应该显式调用对象的析构函数来解除构造函数的影响:

class Widget{
public:
	Widget(int widgetSize);
};

Widget * constructWidgetInBuffer(void *buffer, int widgetSize){
	return new(buffer)Widget(widgetSize);
}


// 在共享内存中分配和释放内存的函数
void *mallocShared(size_t size);
void freeShared(void *memory);
void *sharedMemory = mallocShared(sizeof(Widget));

Widget *pw = constructWidgetInBuffer(sharedMemory , 10);
//...
//delete pw; //结果不确定!共享内存来自mallocShared,不是operator new
pw->~Widget():  //正确,析构pw指向的Widget,但是不释放包含Widget的内存
freeShared(pw);  //正确,释放pw指向的共享内存但是没有调用析构函数
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值