为什么要在 C++ 中为抽象类声明虚拟析构函数?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/270917/
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 14:12:17  来源:igfitidea点击:

Why should I declare a virtual destructor for an abstract class in C++?

c++inheritancevirtual-destructor

提问by Kevin

I know it is a good practice to declare virtual destructors for base classes in C++, but is it always important to declare virtualdestructors even for abstract classes that function as interfaces? Please provide some reasons and examples why.

我知道在 C++ 中为基类声明虚拟析构函数是一种很好的做法,但是virtual即使对于用作接口的抽象类,声明析构函数也总是很重要吗?请提供一些原因和示例。

回答by Airsource Ltd

It's even more important for an interface. Any user of your class will probably hold a pointer to the interface, not a pointer to the concrete implementation. When they come to delete it, if the destructor is non-virtual, they will call the interface's destructor (or the compiler-provided default, if you didn't specify one), not the derived class's destructor. Instant memory leak.

对于界面来说,它甚至更重要。您的类的任何用户都可能持有指向接口的指针,而不是指向具体实现的指针。当他们来删除它时,如果析构函数是非虚拟的,他们将调用接口的析构函数(或编译器提供的默认值,如果您没有指定),而不是派生类的析构函数。即时内存泄漏。

For example

例如

class Interface
{
   virtual void doSomething() = 0;
};

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

void myFunc(void)
{
   Interface* p = new Derived();
   // The behaviour of the next line is undefined. It probably 
   // calls Interface::~Interface, not Derived::~Derived
   delete p; 
}

回答by Johannes Schaub - litb

The answer to your question is often, but not always. If your abstract class forbids clients to call delete on a pointer to it (or if it says so in its documentation), you are free to not declare a virtual destructor.

您的问题的答案通常是,但并非总是如此。如果您的抽象类禁止客户端对指向它的指针调用 delete(或者如果它在其文档中这样说),您可以自由地不声明虚拟析构函数。

You can forbid clients to call delete on a pointer to it by making its destructor protected. Working like this, it is perfectly safe and reasonable to omit a virtual destructor.

您可以通过保护析构函数来禁止客户端对指向它的指针调用 delete。像这样工作,省略虚拟析构函数是完全安全和合理的。

You will eventually end up with no virtual method table, and end up signalling your clients your intention on making it non-deleteable through a pointer to it, so you have indeed reason not to declare it virtual in those cases.

您最终将没有虚拟方法表,并最终向您的客户表明您打算通过指向它的指针使其不可删除,因此在这些情况下您确实有理由不将其声明为虚拟的。

[See item 4 in this article: http://www.gotw.ca/publications/mill18.htm]

[见本文第 4 项:http: //www.gotw.ca/publications/mill18.htm]

回答by davidag

I decided to do some research and try to summarise your answers. The following questions will help you to decide what kind of destructor you need:

我决定做一些研究并尝试总结您的答案。以下问题将帮助您决定需要哪种析构函数:

  1. Is your class intended to be used as a base class?
    • No: Declare public non-virtual destructor to avoid v-pointer on each object of the class *.
    • Yes: Read next question.
  2. Is your base class abstract? (i.e. any virtual pure methods?)
    • No: Try to make your base class abstract by redesigning your class hierarchy
    • Yes: Read next question.
  3. Do you want to allow polymorphic deletion through a base pointer?
    • No: Declare protected virtual destructor to prevent the unwanted usage.
    • Yes: Declare public virtual destructor (no overhead in this case).
  1. 您的类是否打算用作基类?
    • 否:声明公共非虚拟析构函数以避免在类* 的每个对象上使用 v 指针。
    • 是:阅读下一个问题。
  2. 你的基类是抽象的吗?(即任何虚拟纯方法?)
    • 否:尝试通过重新设计类层次结构来使基类抽象
    • 是:阅读下一个问题。
  3. 是否允许通过基指针进行多态删除?
    • 否:声明受保护的虚拟析构函数以防止不必要的使用。
    • 是:声明公共虚拟析构函数(在这种情况下没有开销)。

I hope this helps.

我希望这有帮助。

*It is important to note that there is no way in C++ to mark a class as final (i.e. non subclassable), so in the case that you decide to declare your destructor non-virtual and public, remember to explicitly warn your fellow programmers against deriving from your class.

*需要注意的是,在 C++ 中没有办法将类标记为 final(即不可子类化),因此如果您决定将析构函数声明为非虚拟和公共的,请记住明确警告您的其他程序员来自你的班级。

References:

参考:

回答by OJ.

Yes it is always important. Derived classes may allocate memory or hold reference to other resources that will need to be cleaned up when the object is destroyed. If you do not give your interfaces/abstract classes virtual destructors, then every time you delete a derived class instance via a base class handle your derived class' destructor will not be called.

是的,这总是很重要的。派生类可能会分配内存或持有对其他资源的引用,这些资源在对象被销毁时需要清理。如果你不给你的接口/抽象类虚拟析构函数,那么每次你通过基类句柄删除派生类实例时,你的派生类的析构函数都不会被调用。

Hence, you're opening up the potential for memory leaks

因此,您打开了内存泄漏的可能性

class IFoo
{
  public:
    virtual void DoFoo() = 0;
};

class Bar : public IFoo
{
  char* dooby = NULL;
  public:
    virtual void DoFoo() { dooby = new char[10]; }
    void ~Bar() { delete [] dooby; }
};

IFoo* baz = new Bar();
baz->DoFoo();
delete baz; // memory leak - dooby isn't deleted

回答by Evan Teran

It is not alwaysrequired, but I find it to be good practice. What it does, is it allows a derived object to be safely deleted through a pointer of a base type.

它并不总是必需的,但我发现这是一种很好的做法。它的作用是允许通过基类型的指针安全地删除派生对象。

So for example:

例如:

Base *p = new Derived;
// use p as you see fit
delete p;

is ill-formed if Basedoesn't have a virtual destructor, because it will attempt to delete the object as if it were a Base *.

如果Base没有虚拟析构函数,则格式错误,因为它会尝试删除对象,就好像它是Base *.

回答by JaredPar

It's not only good practice. It is rule #1 for any class hierarchy.

这不仅是好的做法。这是任何类层次结构的规则#1。

  1. The base most class of a hierarchy in C++ must have a virtual destructor
  1. C++中层次结构的最基类必须有一个虚拟析构函数

Now for the Why. Take the typical animal hierarchy. Virtual destructors go through virtual dispatch just as any other method call. Take the following example.

现在是为什么。以典型的动物等级制度为例。虚拟析构函数就像任何其他方法调用一样通过虚拟分派。以下面的例子为例。

Animal* pAnimal = GetAnimal();
delete pAnimal;

Assume that Animal is an abstract class. The only way that C++ knows the proper destructor to call is via virtual method dispatch. If the destructor is not virtual then it will simply call Animal's destructor and not destroy any objects in derived classes.

假设 Animal 是一个抽象类。C++ 知道要调用的正确析构函数的唯一方法是通过虚方法分派。如果析构函数不是虚拟的,那么它将简单地调用 Animal 的析构函数而不破坏派生类中的任何对象。

The reason for making the destructor virtual in the base class is that it simply removes the choice from derived classes. Their destructor becomes virtual by default.

在基类中将析构函数设为虚拟的原因是它只是从派生类中删除了选择。默认情况下,它们的析构函数变为虚拟的。

回答by fatma.ekici

The answer is simple, you need it to be virtual otherwise the base class would not be a complete polymorphic class.

答案很简单,你需要它是虚拟的,否则基类不会是一个完整的多态类。

    Base *ptr = new Derived();
    delete ptr; // Here the call order of destructors: first Derived then Base.

You would prefer the above deletion, but if the base class's destructor is not virtual, only the base class's destructor will be called and all data in derived class will remain undeleted.

你更喜欢上面的删除,但如果基类的析构函数不是虚拟的,那么只会调用基类的析构函数,派生类中的所有数据都不会被删除。