【问题标题】:how are `delete[] obj` and `delete obj` implemented at compiler level [duplicate]`delete[] obj` 和 `delete obj` 如何在编译器级别实现 [重复]
【发布时间】:2013-01-27 22:58:29
【问题描述】:

可能重复:
Why [] is used in delete ( delete [] ) to free dynamically allocated array?
Why does C++ still have a delete[] AND a delete operator?

我想知道它们有什么区别,我知道一些人可能会说的明显答案,一个是删除一个数组,另一个是删除一个对象,但我想知道为什么应该有两种不同的删除方法这两个操作?我的意思是删除基本上是使用 C free 方法实现的,它不关心指针实际上是指向数组还是单个对象。我能想到的唯一原因是两个能够知道它是否是一个数组并为每个单元格调用析构函数,而不仅仅是第一个对象,但这也是不可能的,因为编译器无法猜测数组的长度只是看着它指针。顺便说一句,虽然据说会调用未定义的行为来调用 delete 以获取使用 new[] 分配的内存,但我无法想象任何可能出错的事情。

【问题讨论】:

  • 请注意,它们不是在编译器级别实现的,而是在标准库中作为 ::operator delete(void*)::operator delete[](void*) 实现的
  • @Zaffy:不完全是。你在谈论默认的释放功能。一方面,它们只是默认值,另一方面,释放只是 delete 表达式的最后一部分。
  • 您可能会发现阅读 Itianum C++ ABI 的“数组 cookie”部分很有趣。

标签: c++ memory-management


【解决方案1】:

正如您所发现的,编译器需要知道数组的长度(至少对于非平凡类型)才能为每个元素调用析构函数。对于这个 new[] 通常会分配一些额外的字节来记录元素计数,并返回一个指向这个 bookkeeping 区域末尾的指针。

当你使用delete[]时,编译器会查看数组之前的内存来查找计数并调整指针,从而释放最初分配的块。

如果您使用delete 来销毁动态分配的数组,则不会调用元素的析构函数(第一个除外),通常这将最终尝试释放不指向数组开头的指针分配的块,这可能会破坏堆。

【讨论】:

    【解决方案2】:

    但这也不可能,因为编译器无法猜测 数组的长度只是看它的指针

    这不是真的。编译器本身不需要猜测任何东西,但它会根据它看到的运算符决定调用哪个函数来释放内存。有一个单独的函数专门用于释放数组,并且这个函数确实知道要释放的数组的长度,因此它可以适当地调用析构函数。

    它知道数组的长度,因为通常new[] 分配的内存包括数组长度(因为这在分配时是已知的)并返回一个指向仅分配的“可用”内存的指针。当delete[] 被调用时,它知道如何根据指向给定数组可用部分的指针来访问该内存。

    【讨论】:

      【解决方案3】:

      当您使用new[] 分配内存时,编译器不仅需要构造每个元素,还需要跟踪已分配的元素数量。这是delete[] 正常工作所必需的。

      由于newdelete 对标量进行操作,因此它们不需要这样做,并且可以节省一点开销。

      绝对没有要求newdelete[] 兼容,反之亦然。将两者混合是未定义的行为。

      【讨论】:

      • 可能是区分 POD 和非 POD 的好主意
      • @Cheersandhth.-Alf:我想你要找的是std::is_trivially_destructible...
      • @KerrekSB:可能,可能... :-)
      猜你喜欢
      • 2023-02-13
      • 2011-06-07
      • 2012-06-01
      • 2014-04-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-07
      • 2016-08-08
      相关资源
      最近更新 更多