繁体   English   中英

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

[英]Derived class with non-virtual destructor

在任何情况下,派生类都有合法的非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 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
};

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

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

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

关注Herb Sutter的建议

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

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


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

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

解决最新编辑:

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

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

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

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

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

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

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

有时您无法控制基类,并且您被迫从中派生,即使析构函数不是虚拟的。

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

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

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

是的,有:

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 ,它会专门针对这种情况发出警告。

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

一点也不。 这是我的建议,以防止C ++中的子类(如Java中的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
};

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

是,否和否。

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM