【问题标题】:Why is the deletion of an incomplete type defined as "undefined behaviour"?为什么将不完整类型的删除定义为“未定义行为”?
【发布时间】:2014-12-23 20:58:44
【问题描述】:

为什么不完整类型的删除定义为“未定义行为”

来自 C++ 规范; §5.3.5/5;

如果要删除的对象在删除时具有不完整的类类型,并且完整的类具有非平凡的析构函数或释放函数,则行为未定义

给出代码示例(我明白为什么会出错);

class ABC;

int main()
{
    ABC* p = nullptr;
    delete p;
}

为什么当 gcc、clang 和 msvc 都警告它是一个不完整的类型时,它被定义为 未定义的行为?为什么不只是在那个时候出错,即为什么它不是可诊断的错误

【问题讨论】:

  • @Cyber​​。这就是为什么它是一个错误。我想知道为什么它被定义为“未定义的行为”——为什么编译器不需要编译失败?
  • 对于已声明但尚未定义的类中的任何方法,实际上可能会引发该问题。但是,编译器知道析构函数存在并且可能具有特定定义(微不足道的定义),而不是任何其他任意方法。它唯一不知道的是析构函数是否是虚拟的,这可能会改变调用约定(这一点由另一个问题的答案提出)。
  • 我的猜测是规范走向乐观,说这个问题只需要一个警告,希望程序员知道他在做什么,并提供一个符合其期望的定义(析构函数不是虚拟的)。

标签: c++ language-lawyer


【解决方案1】:

因为,正如您的报价所说,如果它具有非平凡的析构函数或释放函数,它只是未定义的行为。如果不完整,编译器不知道是不是这样,所以不知道程序是否定义良好。

【讨论】:

  • 所以基本上你是说唯一定义明确的情况是针对 POD 类型?
  • @Niall:不。您的引述是说,对于任何具有微不足道的析构函数且没有释放函数的类型,它都是明确定义的。
  • @Niall 请注意,一个类完全有可能拥有一个非平凡的复制 ctor(使其成为非 POD)同时拥有一个平凡的 dtor。
【解决方案2】:

表达式delete p; 做了两件事:

  1. 销毁包含*p的完整对象。
  2. 释放用于存储所述对象的内存。

第 2 项可能在您只知道对象的地址时是可能的,没有任何进一步的信息。内存分配器只关心地址。但是确定完整对象的地址可能很困难;你本质上需要保证你实际上提供了一个完整对象的地址。

但还有更多。在释放对象的存储空间之前,您必须运行析构函数(第 1 项)。如果析构函数没有效果,那么不运行析构函数是可以接受的,因为它的行为就像你运行它们一样。但是如果运行析构函数确实有效果,那么省略第 1 项会导致未定义的行为,并且您需要知道完整的类型才能知道如何运行析构函数。顺便说一句,您还需要知道完整的类型才能确定第 2 项的最派生对象的地址。

【讨论】:

  • 所以这里唯一的“定义”是如果析构函数没有效果,然后内存的释放就是所有发生的事情?
  • @Niall:是的,需要澄清的是,通过多态基指针破坏对象始终是“一种效果”。
猜你喜欢
  • 2011-01-31
  • 2017-01-23
  • 2020-02-11
  • 2020-06-24
  • 1970-01-01
  • 2018-07-03
  • 2013-01-10
  • 2015-02-14
  • 1970-01-01
相关资源
最近更新 更多