何时以及为何不应将基类中的析构函数定义为虚拟?

发布于 2024-11-28 12:47:36 字数 785 浏览 0 评论 0原文

下面的这个例子说明了如何防止派生类被复制。它基于一个基类,其中复制构造函数和复制赋值运算符均声明为 private

class Uncopyable
{
protected:
   // allow construction and destruction of derived objects...
   Uncopyable() {}
   ~Uncopyable() {}

private:
   // but prevent copying...
   Uncopyable(const Uncopyable&);
   Uncopyable& operator=(const Uncopyable&);
};

我们可以使用此类,结合私有继承,使类不可复制:

class derived: private Uncopyable
{...};

没有声明为 virtual。

之前,我了解到,

  • 请注意,类 Uncopyable 中的析构函数 基类应该是虚拟
  • 非基类中的析构函数不应该被设为虚拟的。

在此示例中,Uncopyable 的析构函数不是 virtual,但它是从中继承的。这似乎违背了我之前学到的智慧。

何时以及为何不应将基类中的析构函数定义为虚拟

This example below illustrates how to prevent derived class from being copied. It's based on a base class where both the copy constructor and copy assignment operator are declared private.

class Uncopyable
{
protected:
   // allow construction and destruction of derived objects...
   Uncopyable() {}
   ~Uncopyable() {}

private:
   // but prevent copying...
   Uncopyable(const Uncopyable&);
   Uncopyable& operator=(const Uncopyable&);
};

We can use this class, combined with private inheritance, to make classes uncopyable:

class derived: private Uncopyable
{...};

Notice that the destructor in class Uncopyable is not declared as virtual.

Previously, I learned that

  • Destructors in base classes should be virtual.
  • Destructors in non-base classes should not be made virtual.

In this example, the destructor for Uncopyable is not virtual, but it is being inherited from. This seems to go against the wisdom I've learned previously.

When and why should destructor in base class NOT be defined as virtual?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

小…红帽 2024-12-05 12:47:36

如果您可能尝试通过基类型的指针释放派生类型的对象,则基类析构函数只需是虚拟的。因此,如果您仅私有而不是公开继承基类,就像Uncopyable中的情况一样,那么您不需要担心放入虚拟析构函数,因为使用私有继承时,您无法获取指向派生对象的指针并将其存储在指向基类型的指针中。

另一个例子可能是,如果您要使用像这样的 mixin 类,该类使类跟踪对象分配的数量,其中 mixin 是继承来获取行为但不进行多态处理:

template <typename T> class Counter {
public:
    Counter() { ++numInstances; }
    Counter(const Counter&) { ++numInstances );
    ~Counter() { --numInstances; }

    static unsigned getNumInstances() { return numInstances; }

private:
    static unsigned numInstances;
}
template <typename T> unsigned Counter<T>::numInstances = 0;

更一般地,当使用静态多态性时,您不需要虚拟析构函数,因为您从不使用指向基类型的指针来多态地处理类。您仅使用指向派生类型的指针。

可能还有一些其他情况我没有在这里介绍,但这两种情况(私有继承、混合类和静态多态性)涵盖了不需要虚拟析构函数的大部分空间。

A base class destructor only needs to be virtual if you might try deallocating an object of a derived type through a pointer of the base type. Consequently, if you only inherit from the base class privately instead of publicly, as would be the case in Uncopyable, then you don't need to worry about putting in a virtual destructor, because when using private inheritance you can't get a pointer to the derived object and store it in a pointer to the base type.

Another example might be if you were to use a mixin class like this one that makes a class track the number of object allocations, where the mixin is inherited from to acquire behavior but not to be treated polymorphically:

template <typename T> class Counter {
public:
    Counter() { ++numInstances; }
    Counter(const Counter&) { ++numInstances );
    ~Counter() { --numInstances; }

    static unsigned getNumInstances() { return numInstances; }

private:
    static unsigned numInstances;
}
template <typename T> unsigned Counter<T>::numInstances = 0;

More generally, when using static polymorphism, you don't need virtual destructors because you never treat the classes polymorphically using pointers to the base type. You only use a pointer to the derived type.

There are probably a few other cases I didn't cover here, but these two (private inheritance, mixin classes, and static polymorphism) cover much of the space where virtual destructors aren't required.

挽你眉间 2024-12-05 12:47:36

当您将基础设计为不是接口,而是实现细节时(请注意来自Uncopyableprivate继承)。

When you design the base not as interface, but as implementation detail (note the private inheritance from Uncopyable).

锦上情书 2024-12-05 12:47:36

从技术上讲,如果您知道没有人会将其作为 Uncopyable* 删除,但总是将其作为同一子类删除,那么从技术上讲,您不必将析构函数设为虚拟。

是的,这本质上就是@templatetypedef所说的,但我将以一种可能更简单的方式解释它。

所以:如果人们可能会做这样的事情:

void aFunction(Uncopyable* obj) {
    delete obj;
}

那么你应该声明你的析构函数为 virtual (以确保潜在的子类被调用他们的析构函数。

但是,如果你知道人们会像这样删除子类:

class Widget : public Uncopyable
{
  ....
};

void aFunction(Widget* obj) {
   delete obj;
}

那么你不必使析构函数成为虚拟的(因为子类析构函数将被调用)

,至少这是我的理解。

You technically don't have to make your decostructor virtual if you know nobody will delete it as a Uncopyable*, but will always delete it as a subclass of the same.

Yes this is essentially, what @templatetypedef said, but I'm going to explain it in a maybe easier way.

So: if people might do something like this:

void aFunction(Uncopyable* obj) {
    delete obj;
}

Then you should declare your destructor virtual (to make sure potential subclasses get their destructor called.

However, if you know people will be deleting Subclasses like so:

class Widget : public Uncopyable
{
  ....
};

void aFunction(Widget* obj) {
   delete obj;
}

Then you don't have to make your destructor virtual (as the subclasses destructor will be called).

At least, that's my understanding.

寻找我们的幸福 2024-12-05 12:47:36

当你需要你的对象是普通的旧数据,没有虚函数表时。如果我需要的话,我会评论它,因为 99% 的情况下,在基类析构函数中省略“虚拟”只是一个有人想要纠正的错误。

When you need your objects to be plain old data, with no vtable. I'd comment the heck out of it if I ever needed it, as 99% of the time leaving off the 'virtual' in base class destructors is simply a mistake that someone will want to correct.

放血 2024-12-05 12:47:36

一般规则是使析构函数成为公共和虚拟的,或者使析构函数成为受保护的和非虚拟的。在第一种情况下,您的对象使用可破坏的多态性,并且虚拟析构函数将做正确的事情。在第二种情况下,它只会作为实际的子类被销毁,并且仍然做正确的事情。

The general rule is to make your destructor public and virtual, or protected and non-virtual. In the first case, your object use destroyable polymorphically and teh virtual destructor will do the right thing. In the second case it will only be destroyed as the actual child class and still do the right thing.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文