是否有任何情况下派生类可以拥有非 virtual
析构函数?非 virtual
析构函数意味着该类不应作为基类使用。如果派生类拥有非 virtual
析构函数,会像 Java 中的 final
修饰符一样弱化吗?
我特别关注的是基类具有 virtual
析构函数的派生类的情况。
是否有任何情况下派生类可以拥有非 virtual
析构函数?非 virtual
析构函数意味着该类不应作为基类使用。如果派生类拥有非 virtual
析构函数,会像 Java 中的 final
修饰符一样弱化吗?
我特别关注的是基类具有 virtual
析构函数的派生类的情况。
class Base {};
class Derived : public Base {};
Base* b = new Derived;
delete b; // Does not call Derived's destructor!
delete
,那么就不会有问题。但如果是这种情况,您可能会使用组合而不是继承。
一个派生类没有虚析构函数是否就像Java中的final修饰符的弱形式?
不是的,因为虚性传播给了派生类。
class Base
{
public:
virtual ~Base() {}
virtual void Foo() {};
};
class Derived : public Base
{
public:
~Derived() {} // Will also be virtual
void Foo() {}; // Will also be virtual
};
virtual
”性质会传递到派生类中。它确实会传递到派生类中,可以查阅标准文件12.4.7:如果一个类有一个具有虚析构函数的基类,则它的析构函数(无论是用户声明的还是隐式声明的)都是虚的。 - Raedwald如果您从不会调用指向派生类对象的基类指针上的delete,那么拥有一个没有虚析构函数的基类是完全有效的。
指南#:只有在派生类需要调用虚函数的基础实现时,才使虚函数受保护。 仅适用于析构函数的特殊情况:
指南#:基类析构函数应该是public和virtual,或者是protected和nonvirtual。
也许您的问题实际上是:
如果基类析构函数是虚拟的,那么派生类中的析构函数是否需要是虚拟的?
答案是不需要
如果基类析构函数是虚拟的,则派生类析构函数已经隐式地成为虚拟函数了,您不需要将其显式声明为虚拟函数。
针对最新的编辑内容:
编辑:我尤其关注基类拥有虚析构函数的派生类情况。
在这种情况下,无论是否添加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*
。否则,我已经建议,如果基类的析构函数不是虚拟的,它应该是受保护的;这可以防止发生一种未定义行为的情况(通过指向基类的指针进行删除)。实际上,很多基类(例如std::iterator<>
)具有这样的语义,以至于甚至没人想到要创建指向它们的指针;更不用说通过这样的指针删除了。因此,添加保护可能更费力气而得不偿失。
根据您的类的目的,有时将析构函数保护起来但不是虚的是一个好的做法。这基本上意味着:“您不应该通过基类型指针删除派生类的对象”。
是的,有:
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
,它将特别针对此情况发出警告。
在基类中,您可能不想创建虚析构函数?在这种情况下,不需要析构函数。如果您使用指向基类的指针,并在父类中创建非虚析构函数,则编译器会自动生成警告!如果您想创建最终的父类,则可以阻止它。但最好将其标记为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
};
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
};
这是一个完整的编译器代码。没有产生任何警告,也没有使用多余的代码。