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

时间:2011-09-13 14:51:39

标签: c++

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

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

11 个答案:

答案 0 :(得分:66)

  

在任何情况下,它对于派生来说都是合法的   有一个非虚拟析构函数的类?

  

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

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

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或更早版本中没有内置语言机制来阻止子类(*)。无论如何,这不是什么大问题,因为你应该始终prefer composition over inheritance。也就是说,当“is-a”关系比真正的“has-a”关系更有意义时,使用继承。

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

答案 1 :(得分:30)

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

Follow Herb Sutter's Advice

指南#:只有派生类需要调用虚函数的基本实现时,才能使虚函数受到保护。 仅针对析构函数的特殊情况:

指南#:基类析构函数应该是public和virtual,或者是protected和nonvirtual。


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

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

答案 2 :(得分:12)

解决最新的编辑:

  

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

在这种情况下,派生类的析构函数将是虚拟的,无论您是否添加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(),即使成员的名称不同 - 这是析构函数的唯一特性

答案 3 :(得分:4)

你的问题不是很清楚。如果基类有虚拟 析构函数,派生类将有一个,无论如何。不可能 一旦宣布虚拟,就将其关闭。

在某些情况下,从a派生出来是有道理的 没有虚析构函数的类。基地之所以如此 类析构函数应该是虚拟的,这样就可以通过了一个删除 指向基类的指针。如果派生是私有的,那么你没有 担心这一点,因为您的Derived*不会转换为Base*。 否则,我已经看到了如果基类的建议 析构函数不是虚拟的,应该受到保护;这可以防止这一个 未定义行为的情况(通过指向base的指针删除) 可能会发生。然而,在实践中,许多基类(例如, std::iterator<>)具有语义,甚至不会发生 任何人都可以创建指针;更少删除通过这样的 指针。因此,添加保护可能比它的价值更多。

答案 4 :(得分:3)

取决于你班级的目的。有时,保护析构函数是一种好的做法,但不是虚拟的 - 基本上说:“你不应该通过基类型指针删除派生类的对象”

答案 5 :(得分:1)

非虚拟析构函数完全正常,只要您在删除对象时不想将其用作派生类的基指针。

如果您以多态方式派生类,使用基指针传递并存储它然后删除它然后答案为否,请使用虚拟析构函数。

答案 6 :(得分:1)

是的,有:

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中实现了一个警告,专门用于检测delete对具有非虚拟析构函数的多态非终结类的调用,因此如果使用clang -Wdelete-non-virtual-dtor,它将专门针对这种情况发出警告。

答案 7 :(得分:1)

如果你的派生类没有向基类添加任何数据成员,并且有一个空的析构函数体,那么析构函数是否为虚函数并不重要 - 所有派生的析构函数都会调用它无论如何。不推荐这样做,因为在没有意识到这些限制的情况下,某人很容易过来修改课程。

如果您从未尝试通过指向基类的指针删除对象,那么您将是安全的。这是另一个难以执行的规则,应谨慎使用。

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

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

答案 8 :(得分:0)

是,否和否。

虚析构函数与类作为基类或派生类的能力无关。这两者都是合法的。

然而,有一些理由使析构函数成为虚拟的。见这里:http://en.wikipedia.org/wiki/Virtual_destructor#Virtual_destructors。这使得类除了其他之外还有一个虚拟表,如果它还没有。但是,C ++不需要虚拟表来进行继承。

答案 9 :(得分:0)

  

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

完全没有。这是我的建议,以防止C ++中的子类(如Java中的final修饰符);在类中使析构函数成为私有的。然后你可以阻止从中创建子类

答案 10 :(得分:0)

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

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
};

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