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

Rae*_*ald 49 c++

在某种情况下,派生类是否合法拥有非virtual析构函数?非virtual析构函数表示不应将类用作基类.具有virtual派生类的非析构函数是否会像Java final修饰符的弱形式一样?

我特别感兴趣的是派生类的基类有一个virtual析构函数.

In *_*ico 71

在某种情况下,派生类具有非虚拟析构函数是合法的吗?

是.

非虚析构函数表示不应将类用作基类.

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

class Base {};
class Derived : public Base {};

Base* b = new Derived;
delete b; // Does not call Derived's destructor!
Run Code Online (Sandbox Code Playgroud)

如果你不delete按上述方式做,那就没关系了.但如果是这种情况,那么你可能会使用组合而不是继承.

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

不,因为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
};
Run Code Online (Sandbox Code Playgroud)

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

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

  • "`virtual`-ness传播到派生类." 它呢?[参考标准].它确实如此,12.4.7:"如果一个类有一个带有虚拟析构函数的基类,那么它的析构函数(无论是用户还是隐式声明)都是虚拟的. (33认同)
  • 在相关的说明中,如果你有一个非虚拟的Base类,并且你的Derived类中有一些虚方法,那么`Base*b = new Derived(); 删除b;`将是未定义的行为,可能会导致程序崩溃.它看起来很安全,但事实并非如此.(这是因为`b`不会指向`Derived`对象的'start' - 它将被vtable所需的空间所抵消.然后,`delete`将不会在与...完全相同的地址上运行`new`,因此它不是一个有效的免费地址.如果你想在任何地方使用虚拟方法,那么就把虚拟方法放在基础上. (4认同)
  • @AaronMcDaid **它将被vtable所需的空间抵消,它是** vptr **而不是vtable,vtable是每个类,vptr是每个对象。该对象中包含的是vptr。 (2认同)

Alo*_*ave 30

如果您永远不会在指向派生类对象的Base类指针上调用delete,那么使用非虚拟析构函数的Base类是完全有效的.

关注Herb Sutter的建议:

准则#:仅当派生类需要调用虚函数的基本实现时,才能使虚函数受到保护.仅针对析构函数的特殊情况:

准则#:基类析构函数应该是公共的和虚拟的,或者是受保护的和非虚拟的.


也许您的问题实际上是:如果Base类Destructor是虚拟的
,Derived类中的Destructor是否需要是虚拟的?

答案是否定的.
如果Base类析构函数是虚拟的,那么Derived类析构函数已经隐式虚拟,您不需要将其明确指定为虚拟.

  • “如果基类析构函数是虚拟的,则派生类中的析构函数是否需要是虚拟的?” 是的,这确实是我的问题。 (2认同)

Dav*_*eas 12

解决最新编辑:

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

在这种情况下,无论是否添加关键字,派生类的析构函数都是虚拟的virtual:

struct base {
   virtual ~base() {}       // destructor is virtual
};
struct derived : base {
   ~derived() {}            // destructor is also virtual, because it is virtual in base
};
Run Code Online (Sandbox Code Playgroud)

这不仅限于析构函数,如果在类型层次结构中的任何一点将函数成员声明为虚拟,那么同一函数的所有重写(不重载)都将是虚拟的,无论它们是否被声明为.析构函数的特定位是~derived() 覆盖, virtual ~base()即使成员的名称不同 - 这是析构函数的唯一特性.


Jam*_*nze 5

您的问题还不清楚。如果基类具有虚拟析构函数,则派生类将具有一个虚拟析构函数。声明虚拟化后,无法将其关闭。

当然,在某些情况下,从没有虚拟析构函数的类派生是有意义的。基类析构函数应该是虚拟的,以便您可以通过指向基类的指针进行删除。如果派生是私有的,则不必担心,因为您Derived*不会将其转换为Base*。否则,我看到了以下建议:如果基类析构函数不是虚拟的,则应该对其进行保护;这样可以防止发生一种未定义行为(通过指向基数的指针删除)的情况。但是实际上,许多基类(例如 std::iterator<>)的语义使得任何人甚至都不会创建指向它们的指针。更不用说通过此类指针删除了。因此,添加保护可能比投入更多的精力。