C++ 何时使用虚拟析构函数?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/461203/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 15:28:32  来源:igfitidea点击:

When to use virtual destructors?

c++polymorphismshared-ptrvirtual-destructor

提问by Lodle

I have a solid understanding of most OO theory but the one thing that confuses me a lot is virtual destructors.

我对大多数 OO 理论有深刻的理解,但让我很困惑的一件事是虚拟析构函数。

I thought that the destructor always gets called no matter what and for every object in the chain.

我认为无论什么,对于链中的每个对象,总是会调用析构函数。

When are you meant to make them virtual and why?

你打算什么时候让它们虚拟化,为什么?

回答by Luc Touraille

Virtual destructors are useful when you might potentially delete an instance of a derived class through a pointer to base class:

当您可能通过指向基类的指针删除派生类的实例时,虚拟析构函数很有用:

class Base 
{
    // some virtual methods
};

class Derived : public Base
{
    ~Derived()
    {
        // Do some important cleanup
    }
};

Here, you'll notice that I didn't declare Base's destructor to be virtual. Now, let's have a look at the following snippet:

在这里,您会注意到我没有将 Base 的析构函数声明为virtual. 现在,让我们看一下以下代码段:

Base *b = new Derived();
// use b
delete b; // Here's the problem!

Since Base's destructor is not virtualand bis a Base*pointing to a Derivedobject, delete bhas undefined behaviour:

由于基地的析构函数是不是virtualb一个Base*指向一个Derived对象,delete b具有不确定的行为

[In delete b], if the static type of the object to be deleted is different from its dynamic type, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined.

[在delete b]中,如果待删除对象的静态类型与其动态类型不同,则静态类型应为待删除对象的动态类型的基类,且静态类型应具有虚拟析构函数或行为未定义

In most implementations, the call to the destructor will be resolved like any non-virtual code, meaning that the destructor of the base class will be called but not the one of the derived class, resulting in a resources leak.

在大多数实现中,对析构函数的调用将像任何非虚拟代码一样被解析,这意味着将调用基类的析构函数而不是派生类的析构函数,从而导致资源泄漏。

To sum up, always make base classes' destructors virtualwhen they're meant to be manipulated polymorphically.

总而言之,当基类的析构函数virtual要以多态方式进行操作时,请始终创建它们。

If you want to prevent the deletion of an instance through a base class pointer, you can make the base class destructor protected and nonvirtual; by doing so, the compiler won't let you call deleteon a base class pointer.

如果要防止通过基类指针删除实例,可以将基类析构函数设置为protected和nonvirtual;通过这样做,编译器不会让您调用delete基类指针。

You can learn more about virtuality and virtual base class destructor in this article from Herb Sutter.

您可以在Herb Sutter 的这篇文章中了解有关虚拟性和虚拟基类析构函数的更多信息。

回答by Tunvir Rahman Tusher

A virtual constructor is not possible but virtual destructor is possible. Let us experiment.......

虚拟构造函数是不可能的,但虚拟析构函数是可能的。让我们实验一下......

#include <iostream>

using namespace std;

class Base
{
public:
    Base(){
        cout << "Base Constructor Called\n";
    }
    ~Base(){
        cout << "Base Destructor called\n";
    }
};

class Derived1: public Base
{
public:
    Derived1(){
        cout << "Derived constructor called\n";
    }
    ~Derived1(){
        cout << "Derived destructor called\n";
    }
};

int main()
{
    Base *b = new Derived1();
    delete b;
}

The above code output the following:

上面的代码输出如下:

Base Constructor Called
Derived constructor called
Base Destructor called

The construction of derived object follow the construction rule but when we delete the "b" pointer(base pointer) we have found that only the base destructor is called. But this must not happen. To do the appropriate thing, we have to make the base destructor virtual. Now let see what happens in the following:

派生对象的构造遵循构造规则,但是当我们删除“b”指针(基指针)时,我们发现只调用了基析构函数。但这绝不能发生。为了做适当的事情,我们必须使基础析构函数成为虚拟的。现在让我们看看下面会发生什么:

#include <iostream>

using namespace std;

class Base
{ 
public:
    Base(){
        cout << "Base Constructor Called\n";
    }
    virtual ~Base(){
        cout << "Base Destructor called\n";
    }
};

class Derived1: public Base
{
public:
    Derived1(){
        cout << "Derived constructor called\n";
    }
    ~Derived1(){
        cout << "Derived destructor called\n";
    }
};

int main()
{
    Base *b = new Derived1();
    delete b;
}

The output changed as following:

输出更改如下:

Base Constructor Called
Derived Constructor called
Derived destructor called
Base destructor called

So the destruction of the base pointer (which takes an allocation on derived object!) follows the destruction rule, i.e first the Derived, then the Base. On the other hand, there is nothing like a virtual constructor.

所以基指针的销毁(在派生对象上进行分配!)遵循销毁规则,即首先是派生的,然后是基类。另一方面,没有什么比得上虚拟构造函数了。

回答by Bill the Lizard

Declare destructors virtual in polymorphic base classes. This is Item 7 in Scott Meyers' Effective C++. Meyers goes on to summarize that if a class has anyvirtual function, it should have a virtual destructor, and that classes not designed to be base classes or not designed to be used polymorphically should notdeclare virtual destructors.

在多态基类中将析构函数声明为 virtual。这是 Scott Meyers 的Effective C++ 中的第 7 项。迈尔斯继续总结,如果一个类有任何虚函数,它应该有一个虚析构函数,而不是类设计为基类或不是设计用于多态应声明虚析构函数。

回答by BigSandwich

Also be aware that deleting a base class pointer when there is no virtual destructor will result in undefined behavior. Something that I learned just recently:

另请注意,在没有虚拟析构函数时删除基类指针将导致未定义的行为。我最近学到的东西:

How should overriding delete in C++ behave?

在 C++ 中重写 delete 应该如何表现?

I've been using C++ for years and I still manage to hang myself.

我已经使用 C++ 多年了,但我仍然设法上吊。

回答by yesraaj

Make the destructor virtual whenever your class is polymorphic.

只要您的类是多态的,就将析构函数设为虚拟。

回答by Abyx

Calling destructor via a pointer to a base class

通过指向基类的指针调用析构函数

struct Base {
  virtual void f() {}
  virtual ~Base() {}
};

struct Derived : Base {
  void f() override {}
  ~Derived() override {}
};

Base* base = new Derived;
base->f(); // calls Derived::f
base->~Base(); // calls Derived::~Derived

Virtual destructor call is no different from any other virtual function call.

虚拟析构函数调用与任何其他虚拟函数调用没有区别。

For base->f(), the call will be dispatched to Derived::f(), and it's the same for base->~Base()- its overriding function - the Derived::~Derived()will be called.

对于base->f(),调用将被分派到Derived::f(),并且对于base->~Base()- 它的覆盖函数 -Derived::~Derived()将被调用是相同的。

Same happens when destructor is being called indirectly, e.g. delete base;. The deletestatement will call base->~Base()which will be dispatched to Derived::~Derived().

当间接调用析构函数时也会发生同样的情况,例如delete base;. 该delete语句将调用base->~Base()which 将被分派到Derived::~Derived().

Abstract class with non-virtual destructor

具有非虚拟析构函数的抽象类

If you are not going to delete object through a pointer to its base class - then there is no need to have a virtual destructor. Just make it protectedso that it won't be called accidentally:

如果您不打算通过指向其基类的指针来删除对象 - 那么就没有必要使用虚拟析构函数。只要让它protected不会被意外调用:

// library.hpp

struct Base {
  virtual void f() = 0;

protected:
  ~Base() = default;
};

void CallsF(Base& base);
// CallsF is not going to own "base" (i.e. call "delete &base;").
// It will only call Base::f() so it doesn't need to access Base::~Base.

//-------------------
// application.cpp

struct Derived : Base {
  void f() override { ... }
};

int main() {
  Derived derived;
  CallsF(derived);
  // No need for virtual destructor here as well.
}

回答by Dragan Ostojic

I like to think about interfaces and implementations of interfaces. In C++ speak interface is pure virtual class. Destructor is part of the interface and expected to implemented. Therefore destructor should be pure virtual. How about constructor? Constructor is actually not part of the interface because object is always instantiated explicitly.

我喜欢考虑接口和接口的实现。在 C++ 中,接口是纯虚类。析构函数是接口的一部分,预计会实现。因此析构函数应该是纯虚拟的。构造函数呢?构造函数实际上不是接口的一部分,因为对象总是显式实例化。

回答by Mukul Kashmira

Virtual keyword for destructor is necessary when you want different destructors should follow proper order while objects is being deleted through base class pointer. for example:

当您希望在通过基类指针删除对象时不同的析构函数应遵循正确的顺序时,析构函数的虚拟关键字是必需的。例如:

Base *myObj = new Derived();
// Some code which is using myObj object
myObj->fun();
//Now delete the object
delete myObj ; 

If your base class destructor is virtual then objects will be destructed in a order(firstly derived object then base ). If your base class destructor is NOT virtual then only base class object will get deleted(because pointer is of base class "Base *myObj"). So there will be memory leak for derived object.

如果您的基类析构函数是虚拟的,那么对象将按顺序销毁(首先是派生对象,然后是基类)。如果您的基类析构函数不是虚拟的,那么只会删除基类对象(因为指针属于基类“Base *myObj”)。所以派生对象会有内存泄漏。

回答by Prakash GiBBs

To be simple, Virtual destructor is to destruct the resources in a proper order, when you delete a base class pointer pointing to derived class object.

简单来说,Virtual析构函数就是在删除一个指向派生类对象的基类指针时,按照适当的顺序对资源进行析构。

 #include<iostream>
 using namespace std;
 class B{
    public:
       B(){
          cout<<"B()\n";
       }
       virtual ~B(){ 
          cout<<"~B()\n";
       }
 };
 class D: public B{
    public:
       D(){
          cout<<"D()\n";
       }
       ~D(){
          cout<<"~D()\n";
       }
 };
 int main(){
    B *b = new D();
    delete b;
    return 0;
 }

OUTPUT:
B()
D()
~D()
~B()

==============
If you don't give ~B()  as virtual. then output would be 
B()
D()
~B()
where destruction of ~D() is not done which leads to leak


回答by Trantor

Virtual base class destructors are "best practice" - you should always use them to avoid (hard to detect) memory leaks. Using them, you can be sure all destructors in the inheritance chain of your classes are beeing called (in proper order). Inheriting from a base class using virtual destructor makes the destructor of the inheriting class automatically virtual, too (so you do not have to retype 'virtual' in the inheriting class destructor declaration).

虚拟基类析构函数是“最佳实践”——您应该始终使用它们来避免(难以检测)内存泄漏。使用它们,您可以确保类继承链中的所有析构函数都被调用(以正确的顺序)。使用虚析构函数从基类继承也会使继承类的析构函数自动成为虚函数(因此您不必在继承类析构函数声明中重新键入“virtual”)。