具有非虚析构函数的派生类

Derived class with non-virtual destructor

本文关键字:派生 析构函数      更新时间:2023-10-16

是否存在派生类具有非virtual析构函数是合法的情况?非virtual析构函数表示一个类不应该用作基类。派生类的非virtual析构函数会像Java final修饰符的弱形式一样吗?

我对派生类的基类具有virtual析构函数的情况特别感兴趣。

是否有任何情况下,它是合法的派生类的非虚析构函数?

是的。

非虚析构函数表示类不应用作一个基类。

不是真的;非虚析构函数表示通过base指针删除derived的实例将不起作用。例如:

class Base {};
class Derived : public Base {};
Base* b = new Derived;
delete b; // Does not call Derived's destructor!

如果您不以上述方式执行delete,那么它将很好。但如果是这种情况,那么您可能会使用复合而不是继承。

派生类的非虚析构函数是否会像Java最终修饰符的弱形式?

不,因为virtual -ness传播到派生类。

class Base
{
public:
    virtual ~Base() {}
    virtual void Foo() {};
};
class Derived : public Base
{
public:
    ~Derived() {}  // Will also be virtual
    void Foo() {}; // Will also be virtual
};

在c++ 03或更早版本中没有内置的语言机制来防止子类(*)。这并不是什么大问题,因为比起继承,您应该更喜欢组合。也就是说,当"is-a"关系比真正的"has-a"关系更有意义时,使用继承。

(*)'final'修饰符是在c++ 11中引入的

如果你永远不打算在指向派生类对象的基类指针上调用delete,那么让基类带有非虚析构函数是完全有效的。

遵循Herb Sutter的建议:

指南#:只有当派生类需要调用虚函数的基类实现时,才将虚函数设置为protected。仅对于析构函数的特殊情况:

准则#:基类析构函数应该是public和virtual,或者是protected和non - virtual。


也许你的问题实际上是:
如果基类析构函数是虚的,那么派生的类中的析构函数需要是虚的吗?



如果基类析构函数是虚的,那么派生类析构函数已经是隐式虚的,不需要显式地将其指定为虚的。

处理最新编辑:

编辑:我对派生类的基类有虚析构函数的情况特别感兴趣。

在这种情况下,派生类的析构函数将为虚函数,无论是否添加virtual关键字:
struct base {
   virtual ~base() {}       // destructor is virtual
};
struct derived : base {
   ~derived() {}            // destructor is also virtual, because it is virtual in base
};

这不仅限于析构函数,如果在类型层次结构的任何位置将函数成员声明为虚函数,则该函数的所有重写(而不是重载)都将是虚函数,无论是否声明为虚函数。析构函数的特殊之处在于,即使成员的名称不同,~derived() 也会覆盖 virtual ~base()——这是析构函数的唯一特殊之处。

你的问题不太清楚。如果基类有虚析构函数,派生类将有一个。不可能一旦声明了虚拟性,就关闭虚拟性。

当然在某些情况下从a推导出来是有意义的没有虚析构函数的类。基地的原因类析构函数应该是虚的,这样就可以通过基类的指针。如果推导是私有的,就不需要不要担心这一点,因为您的Derived*不会转换为Base*。否则,我已经看到了建议,如果基类析构函数不是虚函数,它应该被保护;这可以防止未定义行为(通过指向基的指针进行删除)的情况可能会发生。然而,在实践中,许多基类(例如。std::iterator<>)有这样的语义,它甚至不会发生创建指向它们的指针;更不用说通过这种方式删除了指针。因此,增加保护措施可能比它的价值更多。

取决于你的类的目的。有时使析构函数受保护是一个好做法,但不是虚拟的——这基本上是说:"你不应该通过基类型指针删除派生类的对象"

如果您的派生类没有向基类添加任何数据成员,并且有一个空析构函数体,那么析构函数是否是虚函数就无关紧要了——派生析构函数所要做的就是调用基类。不建议这样做,因为很容易有人在不知道这些限制的情况下修改类。

如果您从不尝试通过指向基类的指针删除对象,则是安全的。这是另一个很难执行的规则,应该小心使用。

有时你对基类没有任何控制,你被迫从它派生,即使析构函数不是虚的。

最后,在基类中使用非虚析构函数不会对派生类施加任何编译器强制执行的限制,因此我认为它根本不像Java的final。

是的,有:

void dothis(Base const&);
void foo() {
  Derived d;
  tothis(d);
}

这里的类是多态使用的,但是没有调用delete,因此很好。

另一个例子是:
std::shared_ptr<Base> create() { return std::shared_ptr<Base>(new Derived); }

因为shared_ptr可以使用非多态的delete(通过类型擦除)。

我在Clang中实现了一个警告,专门用于检测对具有非虚拟析构函数的多态非final类的delete调用,因此如果您使用clang -Wdelete-non-virtual-dtor,它将专门针对这种情况发出警告。

非虚析构函数是完全可以的,只要你不想在删除对象时将其用作派生类的基指针。

如果你以一种多态的方式构造派生类,用基指针传递和存储它,然后删除它,那么答案是不,使用虚析构函数。

是的,不,不。

虚析构函数与类是基类还是派生类无关。它是合法的。

然而,有一定的理由使析构函数为虚函数。请看这里:http://en.wikipedia.org/wiki/Virtual_destructor#Virtual_destructors。这使得一个类拥有一个虚拟表,如果它还没有的话。然而,c++并不需要虚表来进行继承。

派生类的非虚析构函数会像Java final修饰符的弱形式一样吗?

一点也不。这里是我的建议,以防止子类在c++中(如Java中的final修饰符);在类中使析构函数为私有。然后你可以防止从它创建子类

你可能不想在基类中创建虚析构函数?在这种情况下没有析构函数。如果使用指向基类的指针并在父类中创建非虚析构函数,编译器将自动生成此警告!如果你想创建final父类,你可以阻塞它。但最好是将其标记为final,如:

class Base{
public:
    //No virtual destructor defined
    virtual void Foo() {};
};
class Derived final : public Base{
public:
    ~Derived() {}  // define some non-virtual destructor
    void Foo() {}; // Will also be virtual
};

在这种情况下,编译器知道你想要什么,不会产生警告。空虚基析构函数的决定不太好,但可以接受。您不需要设置属性为final。但这不是你想要的。您也不需要定义空的虚基方法Foo最好的是:

class Base{
public:
  //No virtual destructor defined
  virtual void Foo() = 0; // abstract method
};
class Derived final : public Base{
public:
  ~Derived() {}  // define some non-virtual destructor
  void Foo() {}; // Will also be virtual
};

这是编译器完整清晰的代码。不生成警告,也不使用备用代码。