为什么,真的,删除不完整的类型是未定义的行为? [英] Why, really, deleting an incomplete type is undefined behaviour?

查看:412
本文介绍了为什么,真的,删除不完整的类型是未定义的行为?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑这个经典例子用来解释与转发声明不同:

Consider this classic example used to explain what not to do with forward declarations:

//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的析构函数是不重要的。
我想要了解的是这个的根本原因。

I understand from the standard that this case is headed towards UB since Body's destructor is non trivial. What I'm trying to understand is really the root cause of this.

我的意思是,问题似乎是触发的事实,Handle的dtor是内联的,因此编译器执行类似下面的inline expansion(这里几乎是伪代码)。

I mean, the problem seems to be "triggered" by the fact that Handle's dtor is inline, and so the compiler does something like the following "inline expansion" (almost pseudo-code here).

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

在所有翻译单元(仅 Handle_user.cpp 在这种情况下)其中一个Handle实例被销毁,对吧?
我只是不能理解这个:确定,当生成以上内联扩展编译器没有一个完整的定义的Body类,但为什么不能简单地让链接器解析为 impl _->〜Body()事情,因此它调用了实际上在其实现文件中定义的Body的析构函数。

In all translation units (only Handle_user.cpp in this case) where a Handle instance gets to be destroyed, right? I just can't understand this: ok, when generating the above inline expansion the compiler doesn't have a full definition of the Body class, but why cannot it simply have the linker resolve for the impl_->~Body() thing and so have it call the Body's destructor function that's actually defined in its implementation file?

换句话说:我理解,在Handle破坏的时候,编译器甚至不知道对于Body是否存在一个(非平凡的)析构函数,但是为什么它不能像它总是那样,那就是留下一个占位符,并且如果该函数真的不可用,最终有一个链接器未解析的外部?

In other words: I understand that at the point of Handle destruction the compiler doesn't even know if a (non-trivial) destructor exists or not for Body, but why can't it do as it always does, that is leave a "placeholder" for the linker to fill in, and eventually have a linker "unresolved external" if that function is really not available?

我在这里缺少一些东西那个case对于愚蠢的问题对不起)?
如果不是这样,我只是想知道这背后的理由。

Am I missing something big here (and in that case sorry for the stupid question)? If that's not the case, I'm just curious to understand the rationale behind this.

推荐答案

答案和添加我自己,没有类定义调用代码不知道:

To combine several answers and add my own, without a class definition the calling code doesn't know:


  • 类是否有一个声明的析构函数,如果是,则默认析构函数是否是微不足道的,

  • 是否调用代码可访问析构函数

  • 哪些基类存在并具有析构函数,

  • 析构函数是否为虚函数。虚拟函数调用实际上使用不同于非虚拟函数调用的调用约定。编译器不能只是发出代码调用〜Body,并让链接器稍后解决细节,

  • (这只是在,感谢GMan)是否 delete 为该类重载。

  • whether the class has a declared destructor, or if the default destructor is to be used, and if so whether the default destructor is trivial,
  • whether the destructor is accessible to the calling code,
  • what base classes exist and have destructors,
  • whether the destructor is virtual. Virtual function calls in effect use a different calling convention from non-virtual ones. The compiler can't just "emit the code to call ~Body", and leave the linker to work out the details later,
  • (this just in, thanks GMan) whether delete is overloaded for the class.

不完全类型的一些或所有这些原因(加上另一个不适用于析构函数 - 你不知道参数或返回类型)。析构函数没有什么不同。所以我不确定你的意思是什么意思,当你说为什么不能像它总是做。

You can't call any member function on an incomplete type for some or all of those reasons (plus another that doesn't apply to destructors - you wouldn't know the parameters or return type). A destructor is no different. So I'm not sure what you mean when you say "why can't it do as it always does?".

如你所知,解决方案是在定义 Body 的TU中定义 Handle 的析构函数,与定义每个其他成员函数 Handle ,它调用函数或使用 Body 的数据成员。然后在 delete impl _; 被编译的时候,所有的信息都可以发出该调用的代码。

As you already know, the solution is to define the destructor of Handle in the TU which has the definition of Body, same place as you define every other member function of Handle which calls functions or uses data members of Body. Then at the point where delete impl_; is compiled, all the information is available to emit the code for that call.

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

Note that the standard actually says, 5.3.5/5:


如果被删除的对象有
不完整类类型在
删除点处,完整类具有
非平凡析构函数或
释放函数,行为是
未定义。

if the object being deleted has incomplete class type at the point of deletion and the complete class has a non-trivial destructor or a deallocation function, the behavior is undefined.

我假设这是你可以删除一个不完整的POD类型,就像你在中的一样。 g ++给你一个非常严厉的警告,如果你尝试它。

I presume this is so that you can delete an incomplete POD type, same as you could free it in C. g++ gives you a pretty stern warning if you try it, though.

这篇关于为什么,真的,删除不完整的类型是未定义的行为?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆