简体   繁体   English

虚拟析构函数在特定情况下的实际必要性

[英]virtual destructor's practical necessity in a particular case

C++03 5.3.5.3 C ++ 03 5.3.5.3

In the first alternative (delete object), if the static type of the operand is different from its dynamic type, the static type shall be a base class of the operand's dynamic type and the static type shall have a virtual destructor or the behavior is undefined. 在第一个替代方案(删除对象)中,如果操作数的静态类型不同于其动态类型,则静态类型应为操作数动态类型的基类,并且静态类型应具有虚拟析构函数或行为未定义。

This is the theory. 这是理论。 The question, however, is a practical one. 但是,这个问题是一个实际的问题。 What if the derived class adds no data members? 如果派生类不添加数据成员怎么办?

struct Base{
   //some members
   //no virtual functions, no virtual destructor
};
struct Derived:Base{
   //no more data members
   //possibly some more nonvirtual member functions
};

int main(){
     Base* p = new Derived;
     delete p; //UB according to the quote above
}

The question: is there any existing implementation on which this would really be dangerous? 问题是: 是否存在任何现有的实施方式,但这样做确实很危险? If so, could you please describe how the internals are implemented in that implementation which makes this code crash/leak or whatever? 如果是这样,您能否描述在该实现中如何实现内部结构,从而使此代码崩溃/泄漏或其他原因? I beg you to believe, I swear that I have no intentions to rely on this behavior :) 恳求您相信,我发誓我无意依靠这种行为:)

One example is if you provide a custom operator new in struct Derived . 一个示例是,如果您提供struct Derived operator new的自定义operator new Obviously calling wrong operator delete will likely produce devastating results. 显然,调用错误的operator delete可能会产生毁灭性的结果。

I know of no implementation on which the above would be dangerous, and I think it unlikely that there ever will be such an implementation. 我不知道会发生上述危险的任何实现方式,并且我认为永远不会有这样的实现方式。

Here's why: 原因如下:

"undefined behaviour" is a catch-all phrase meaning (as everyone knows), anything could happen. “不确定的行为”是一个笼统的短语,意思是(众所周知),任何事情都可能发生。 The code could eat your lunch, or do nothing at all. 该代码可以吃掉你的午餐,或者什么都不做。

However, compiler writers are sane people, and there's a difference between undefined behaviour at compile-time, and undefined behaviour at run-time. 但是,编译器编写者是个理智的人,在编译时的未定义行为与运行时的未定义行为之间是有区别的。 If I was writing a compiler for an implementation where the code snippet above was dangerous, it would be easy to catch and prevent at compile time. 如果我正在为上面的代码段很危险的实现编写编译器, 那么在编译时很容易捕获和阻止。 I can says it's a compilation error (or warning, maybe): Error 666: Cannot derive from class with non-virtual destructor. 我可以说这是一个编译错误(或可能是警告): Error 666: Cannot derive from class with non-virtual destructor.

I think I'm allowed to do that, because the compiler's behaviour in this case is not defined by the standard. 我认为我可以这样做,因为在这种情况下编译器的行为不是由标准定义的。

I can't answer for specific compilers, you'd have to ask the compiler writers. 对于特定的编译器,我无法回答,您必须询问编译器作者。 Even if a compiler works now, it might not do so in the next version so I would not rely on it. 即使编译器现在可以工作,在下一版本中它也可能不工作,因此我不会依赖它。

Do you need this behaviour? 您需要这种行为吗?

Let me guess that 我猜一下

  1. You want to be able to have a base class pointer without seeing the derived class and 您希望能够有一个基类指针,而不必看到派生类,并且
  2. Not have a v-table in Base and 在Base中没有v表,并且
  3. Be able to clean up in the base class pointer. 能够在基类指针中清理。

If those are your requirements it is possible to do, with boost::shared_ptr or your own adaptation. 如果这些是您的要求,则可以通过boost :: shared_ptr或您自己的修改来实现。

At the point you pass the pointer you pass in a boost::shared_ptr with an actual "Derived" underneath. 在您传递指针的那一点上,您传递了一个boost :: shared_ptr,其下面有一个实际的“派生”。 When it is deleted it will use the destructor that was created when the pointer was created which uses the correct delete. 删除后,它将使用创建指针时使用正确删除创建的析构函数。 You should probably give Base a protected destructor though to be safe. 为了安全起见,您可能应该给Base一个受保护的析构函数。

Note that there still is a v-table but it is in the shared pointer deleter base not in the class itself. 请注意,仍然有一个v表,但是它在共享指针删除器库中,而不在类本身中。

To create your own adaptation, if you use boost::function and boost::bind you don't need a v-table at all. 要创建自己的改编,如果使用boost :: function和boost :: bind,则根本不需要v表。 You just get your boost::bind to wrap the underlying Derived* and the function calls delete on it. 您只要获得boost :: bind来包装基础的Derived *,函数就会对其进行删除。

In your particular case, where you do not have any data member declared in the derived class and if you do not have any custom new/delete operators (as mentioned by Sharptooth), you may not have any problems ,but do you guarantee that no user will ever derive your class? 在您的特殊情况下,如果您在派生类中没有声明任何数据成员,并且没有任何自定义的new / delete运算符(如Sharptooth所述),则可能不会有任何问题,但是您可以保证不会用户会得到您的课程吗? If you do not make your Base's destructor virtual, there is no way for any of the classes derived from Derived to call their destructors in case the objects of derived classes are used via a Base pointer. 如果您不对Base's析构函数进行虚拟化,则在通过Base指针使用派生类的对象的情况下,任何派生自Derived的类都无法调用其析构函数。

Also, there is a general notion that if you have virtual functions in your base class, the destructor should be made virtual. 同样,有一个普遍的概念,即如果基类中有虚函数,则应该将析构函数设为虚函数。 So better not surprise anybody :) 所以最好不要让任何人感到惊讶:)

I totally agree with 'Roddy'. 我完全同意“罗迪”。

Unless you're writing the code for perverted compiler designed for a non-existing virtual machine just to prove that so-called undefined behavior can bite - there's no problem. 除非您正在编写为不存在的虚拟机设计的变态编译器的代码,否则只是为了证明所谓的未定义行为会引起误解-不会有问题。

The point of 'sharptooth' about custom new/delete operators is inapplicable here. 关于自定义new/delete运算符的“尖锐”之处在这里不适用。 Because virtual d'tor and won't solve in any way the problem he/she describes. 因为虚拟d'tor并不会以任何方式解决他/她描述的问题。

However it's a good point though. 但是,这是一个好点。 It means that the model where you provide a virtual d'tor and by such enable the polymorphic object creating/deletion is defective by design. 这意味着您在其中提供虚拟数据并通过这种方式启用多态对象创建/删除的模型在设计上是有缺陷的。 A more correct design is to equip such objects with a virtual function that does two things at once: call its (correct) destructor, and also free its memory the way it should be freed. 一种更正确的设计是为此类对象配备一个虚拟函数,该函数一次执行两件事:调用其(正确的)析构函数,并以应释放的方式释放其内存。 In simple words - destroy the object by the appropriate means, which are known for the object itself. 简而言之-用适当的手段销毁对象,这对于对象本身是已知的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM