为什么删除不完整的类型实际上是未定义的行为?

2024-04-08

考虑这个经典的例子来解释什么not与前向声明有关:

//in Handle.h file
class Body;

class Handle
{
   public:
      Handle();
      ~Handle() {delete impl_;}
   //....
   private:
      Body *impl_;
};

//---------------------------------------
//in Handle.cpp file

#include "Handle.h"

class Body 
{
  //Non-trivial destructor here
    public:
       ~Body () {//Do a lot of things...}
};

Handle::Handle () : impl_(new Body) {}

//---------------------------------------
//in Handle_user.cpp client code:

#include "Handle.h"

//... in some function... 
{
    Handle handleObj;

    //Do smtg with handleObj...

    //handleObj now reaches end-of-life, and BUM: Undefined behaviour
} 

我从标准中了解到,这种情况正走向 UB,因为 Body 的析构函数并不平凡。 我试图理解的实际上是造成这种情况的根本原因。

我的意思是,这个问题似乎是由 Handle 的 dtor 是内联的这一事实“触发”的,因此编译器会执行类似于以下“内联扩展”的操作(这里几乎是伪代码)。

inline Handle::~Handle()
{
     impl_->~Body();
     operator delete (impl_);
}

在所有翻译单元中(仅Handle_user.cpp在这种情况下)Handle 实例会被销毁,对吧? 我只是无法理解这一点:好的,当生成上述内联扩展时,编译器没有 Body 类的完整定义,但为什么它不能简单地让链接器解析为impl_->~Body()那么它是否调用了在其实现文件中实际定义的 Body 析构函数?

换句话说:我知道在 Handle 销毁时,编译器甚至不知道 Body 是否存在(非平凡的)析构函数,但为什么它不能像往常一样做,那就是留下一个供链接器填写的“占位符”,如果该函数确实不可用,最终会有一个链接器“未解析的外部”?

我在这里错过了一些大事吗(在这种情况下,我对这个愚蠢的问题感到抱歉)? 如果情况并非如此,我只是想了解其背后的原理。


要组合多个答案并添加我自己的答案,如果没有类定义,调用代码不知道:

  • 该类是否具有声明的析构函数,或者是否使用默认析构函数,如果是,则默认析构函数是否微不足道,
  • 调用代码是否可以访问析构函数,
  • 存在哪些基类并具有析构函数,
  • 析构函数是否是虚拟的。实际上,虚拟函数调用使用与非虚拟函数不同的调用约定。编译器不能只是“发出代码来调用〜Body”,然后让链接器稍后计算细节,
  • (这刚刚进来,谢谢 GMan)是否delete该类已超载。

由于某些或所有这些原因,您不能在不完整类型上调用任何成员函数(加上另一个不适用于析构函数的原因 - 您不知道参数或返回类型)。析构函数也不例外。所以我不确定当你说“为什么它不能像往常那样做?”时你的意思是什么。

如您所知,解决方案是定义析构函数Handle在 TU 中定义为Body,与定义每个其他成员函数的位置相同Handle它调用函数或使用数据成员Body。然后在那个点delete impl_;编译后,所有信息都可用于发出该调用的代码。

请注意,该标准实际上是说 5.3.5/5:

如果被删除的对象有 此时的类类型不完整 删除并且完整的类有一个 非平凡的析构函数或 解除分配函数,其行为是 不明确的。

我认为这是为了让您可以删除不完整的 POD 类型,就像您可以的那样free不过,如果你尝试的话,g++ 会给你一个非常严厉的警告。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么删除不完整的类型实际上是未定义的行为? 的相关文章

  • ROWNUM 的 OracleType 是什么

    我试图参数化所有现有的 sql 但以下代码给了我一个问题 command CommandText String Format SELECT FROM 0 WHERE ROWNUM lt maxRecords command CommandT
  • 在 LINQ 查询中返回不带时间的日期

    我正在编写一个查询 我想计算按日期联系我们的呼叫中心的次数 看起来很简单 但由于联系日期字段是日期时间字段 我得到了时间 因此当我按联系日期 时间 分组时 每个联系日期实例的计数为 1 所以 我想只按日期分组 而不按时间分组 下面是我用来查
  • 创建 DirectoryEntry 实例以供测试使用

    我正在尝试创建 DirectoryEntry 的实例 以便可以使用它来测试将传递 DirectoryEntry 的一些代码 然而 尽管进行了很多尝试 我还是找不到实例化 DE 并初始化它的 PropertyCollection 的方法 我有
  • Func 方法参数的首选命名约定是什么?

    我承认这个问题是主观的 但我对社区的观点感兴趣 我有一个缓存类 它采用类型的缓存加载器函数Func
  • FFMPEG Seeking 带来音频伪影

    我正在使用 ffmpeg 实现音频解码器 在读取音频甚至搜索已经可以工作时 我无法找到一种在搜索后清除缓冲区的方法 因此当应用程序在搜索后立即开始读取音频时 我没有任何工件 avcodec flush buffers似乎对内部缓冲区没有任何
  • 使用 Microsoft Graph API 订阅 Outlook 推送通知时出现 400 错误请求错误

    我正在尝试使用 Microsoft Graph API 创建订阅以通过推送通知获取 Outlook 电子邮件 mentions 我在用本文档 https learn microsoft com en us graph api subscri
  • C# 中值类型和引用类型有什么区别? [复制]

    这个问题在这里已经有答案了 我知道一些差异 值类型存储在堆栈上 而引用类型存储在托管堆上 值类型变量直接包含它们的值 而引用变量仅包含对托管堆上创建的对象位置的引用 我错过了任何其他区别吗 如果是的话 它们是什么 请阅读 堆栈是一个实现细节
  • C# 中可空类型是什么?

    当我们必须使用nullable输入 C net 任何人都可以举例说明 可空类型 何时使用可空类型 https web archive org web http broadcast oreilly com 2010 11 understand
  • 如何在 WPF RichTextBox 中跟踪 TextPointer?

    我正在尝试了解 WPF RichTextBox 中的 TextPointer 类 我希望能够跟踪它们 以便我可以将信息与文本中的区域相关联 我目前正在使用一个非常简单的示例来尝试弄清楚发生了什么 在 PreviewKeyDown 事件中 我
  • 如何针对 Nancy 中的 Active Directory 进行身份验证?

    这是一篇过时的文章 但是http msdn microsoft com en us library ff650308 aspx paght000026 step3 http msdn microsoft com en us library
  • c 中的错误:声明隐藏了全局范围内的变量

    当我尝试编译以下代码时 我收到此错误消息 错误 声明隐藏了全局范围内的变量 无效迭代器 节点 根 我不明白我到底在哪里隐藏或隐藏了之前声明的全局变量 我怎样才能解决这个问题 typedef node typedef struct node
  • 当 Cortex-M3 出现硬故障时如何保留堆栈跟踪?

    使用以下设置 基于 Cortex M3 的 C gcc arm 交叉工具链 https launchpad net gcc arm embedded 使用 C 和 C FreeRtos 7 5 3 日食月神 Segger Jlink 与 J
  • 在 ASP.Net Core 2.0 中导出到 Excel

    我曾经使用下面的代码在 ASP NET MVC 中将数据导出到 Excel Response AppendHeader content disposition attachment filename ExportedHtml xls Res
  • 编译的表达式树会泄漏吗?

    根据我的理解 JIT 代码在程序运行时永远不会从内存中释放 这是否意味着重复调用 Compile 表达式树上会泄漏内存吗 这意味着仅在静态构造函数中编译表达式树或以其他方式缓存它们 这可能不那么简单 正确的 他们可能是GCed Lambda
  • 使用 LINQ 查找列表中特定类型的第一个元素

    使用 LINQ 和 C 在元素列表中查找特定类型的第一个项目的最短表示法是什么 var first yourCollection OfType
  • 可空属性与可空局部变量

    我对以下行为感到困惑Nullable types class TestClass public int value 0 TestClass test new TestClass Now Nullable GetUnderlyingType
  • 在 URL 中发送之前对特殊字符进行百分比编码

    我需要传递特殊字符 如 等 Facebook Twitter 和此类社交网站的 URL 为此 我将这些字符替换为 URL 转义码 return valToEncode Replace 21 Replace 23 Replace 24 Rep
  • 如何构建印度尼西亚电话号码正则表达式

    这些是一些印度尼西亚的电话号码 08xxxxxxxxx 至少包含 11 个字符长度 08xxxxxxxxxxx 始终以 08 开头 我发现这个很有用 Regex regex new Regex 08 0 9 0 9 0 9 0 9 0 9
  • 如何使用 ReactiveList 以便在添加新项目时更新 UI

    我正在创建一个带有列表的 Xamarin Forms 应用程序 itemSource 是一个reactiveList 但是 向列表添加新项目不会更新 UI 这样做的正确方法是什么 列表定义 listView new ListView var
  • 将 viewbag 从操作控制器传递到部分视图

    我有一个带有部分视图的 mvc 视图 控制器中有一个 ActionResult 方法 它将返回 PartialView 因此 我需要将 ViewBag 数据从 ActionResult 方法传递到 Partial View 这是我的控制器

随机推荐