简体   繁体   English

虚拟析构函数:在未动态分配内存时是否需要?

[英]Virtual destructor: is it required when not dynamically allocated memory?

Do we need a virtual destructor if my classes do not allocate any memory dynamically ? 如果我的类没有动态分配任何内存,我们是否需要虚拟析构函数?

eg 例如

class A
{
      private: 
      int a;
      int b;

      public:
      A();
      ~A();
};

class B: public A
{     
      private:
      int c;
      int d;

      public:
      B();
      ~B();
};

In this case do we need to mark A's destructor as virtual ? 在这种情况下,我们是否需要将A的析构函数标记为虚拟?

The issue is not whether your classes allocate memory dynamically. 问题不在于您的类是否动态分配内存。 It is if a user of the classes allocates a B object via an A pointer and then deletes it: 如果这些类的用户通过A指针分配了B对象,然后将其删除:

A * a = new B;
delete a;

In this case, if there is no virtual destructor for A, the C++ Standard says that your program exhibits undefined behaviour. 在这种情况下,如果没有用于A的虚拟析构函数,则C ++标准表示您的程序表现出未定义的行为。 This is not a good thing. 这不是一件好事。

This behaviour is specified in section 5.3.5/3 of the Standard (here referring to delete ): 该行为在标准的5.3.5 / 3节中指定(此处指的是delete ):

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. 如果操作数的静态类型不同于其动态类型,则静态类型应为操作数动态类型的基类,并且静态类型应具有虚拟析构函数或行为未定义。

The purpose of virtual destructor (ie the purpose of making a destructor virtual ) is to facilitate the polymorphic deletion of objects through delete-expression . 虚拟析构函数的目的(即使析构函数虚拟化的目的)是通过delete-expression促进对象的多态删除。 If your design does not call for polymorphic deletion of objects, you don't need virtual destructors. 如果您的设计不要求对象的多态删除,则不需要虚拟析构函数。 Referring to your example, if you'll ever have to delete an object of type B through a pointer of type A * (polymorphic deletion), you'll need virtual destructor as high up in the hierarchy as A . 以您的示例为例,如果您必须通过类型A * (多态删除)的指针删除类型B的对象,则需要在层次结构中与A一样高的虚拟析构函数。 That's how it looks from a formal point of view. 从正式的角度来看就是这样。

(Note, BTW, as Neil said, that what's important is how you create/delete your class objects, not how classes manage their internal memory.) (请注意,顺便说一句,正如Neil所说,重要的是如何创建/删除类对象,而不是类如何管理其内部存储器。)

As for the good programming practices... It depends on your intent and your design in the end. 至于好的编程习惯……这最终取决于您的意图和设计。 If your classes are not designed to be polymorphic at all (no virtual methods whatsoever), then you don't need virtual destructors. 如果您的类根本没有设计成多态的(根本没有虚拟方法),那么您就不需要虚拟析构函数。 If your class is polymorphic (have at least one virtual method), then making the destructor virtual "just in case" might be a very good idea, and in this case it bears virtually zero performance/memory penalty with it. 如果您的类是多态的(至少具有一个虚拟方法),则将析构函数虚拟化为“以防万一”可能是一个很好的主意,在这种情况下,它的性能/内存损失几乎为零。

The latter is usually expressed as a rather well-known good practice guideline: if your class has at least one virtual method, make the destructor virtual as well. 后者通常表示为一个众所周知的良好实践准则:如果您的类至少具有一个虚方法,则也将析构函数也设为虚方法。 Although from the formal point of view a virtual destructor might not be really needed there, it is still a pretty good guideline to follow. 尽管从正式的角度来看,那里实际上并不需要虚拟析构函数,但是仍然可以遵循它。

Classes that have no resources but can form polymorphic hierarchies should always define empty virtual destructors, except that it is perfectly sufficient to define an explicit empty (and even pure) virtual destructor at the very base of the hierarchy. 没有资源但可以形成多态层次结构的类应始终定义空的虚拟析构函数,除非在层次结构的最基础上定义一个显式的空(甚至纯净的)虚拟析构函数是完全足够的。 All other destructors will become virtual automatically, even if they are defined implictly by the compiler. 所有其他析构函数都将自动变为虚拟的,即使它们是由编译器隐式定义的。 Ie you don't have to explicitly define an empty destructor in every class. 也就是说,您不必在每个类中都明确定义一个空的析构函数。 Just the base is enough. 只要基地就足够了。

Freeing memory is not the only critical function a destructor can perform. 释放内存并不是析构函数可以执行的唯一关键功能。 It can also be used to reset global state for instance. 例如,它也可以用于重置全局状态。 Not doing this won't leak memory but could potentially cause other issues in your program. 不这样做将不会泄漏内存,但可能会导致程序中出现其他问题。

Additionally, even if your destructor doesn't do anything useful today, it may at some point in the future. 此外,即使您的析构函数今天没有做任何有用的事情,它也可能在将来的某个时候出现。 There's no real reason to avoid a virtual destructor if you have inheritance so why not just add it and sleep better at night? 如果有继承,没有真正的理由避免使用虚拟析构函数,那么为什么不添加它并在晚上睡得更好呢?

The destructor of the parent class is always automatically called, and the default dtor is always generated if there's no explicit dtor declared. 总是自动调用父类的析构函数,并且如果未声明任何显式dtor,则始终生成默认dtor。 In your example, neither A nor B needs to have a non-trivial dtor. 在您的示例中,A和B都不需要具有非琐碎的dtor。

If you class has virtual functions, an additional virtual dtor doesn't hurt, and is good practice. 如果您的班级具有虚拟功能,则附加的虚拟dtor不会受到伤害,这是一种很好的做法。 In case you class allocates memory or any other resource (like opening a file), a dtor is needed to free that resource again upon destruction. 如果您的班级分配了内存或任何其他资源(例如打开文件),则需要dtor在销毁后再次释放该资源。

The purpose of declaring destructor as virtual is to be able to invoke the derived class's destructor whenever you call delete on a pointer of type Base which is pointing to object of type Derived. 将析构函数声明为虚拟的目的是,每当您在指向类型为Derived的对象的Base类型的指针上调用delete时,便能够调用派生类的析构函数。 Not doing so would result in undefined behavior. 不这样做将导致不确定的行为。

The assumption that you need not mark destructor as virtual if you are not allocating memory dynamically implies that you do not need to call derived class destructor if you are not allocating memory dynamically, which is wrong. 如果您不动态分配内存,则无需将析构函数标记为虚拟,这意味着如果您不动态分配内存,则无需调用派生类析构函数,这是错误的。 As you may still do several other operations in your derived class's destructor other than just deallocating the dynamically allocated memory. 您可能仍会在派生类的析构函数中执行其他一些操作,而不仅仅是释放动态分配的内存。 Examples would be closing an open file, logging some information etc. 例如关闭一个打开的文件,记录一些信息等。

If your only concern is memory, maybe you should start by protecting base class destructor (and/or maybe others). 如果您唯一关心的是内存,那么也许您应该从保护基类析构函数(和/或其他)开始。 Then if something does not compile, you'll see why. 然后,如果某些内容无法编译,您将明白原因。 Ref: boost::any ways. 参考:boost ::任何方式。

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

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