提问人:Raedwald 提问时间:9/13/2011 最后编辑:Raedwald 更新时间:8/26/2023 访问量:91021
具有非虚拟析构函数的派生类
Derived class with non-virtual destructor
问:
在哪些情况下,派生类具有非析构函数是合法的?非析构函数表示不应将类用作基类。派生类的非析构函数会像 Java 修饰符的弱形式一样吗?virtual
virtual
virtual
final
我对派生类的基类具有析构函数的情况特别感兴趣。virtual
答:
是的,否和否。
虚拟析构函数与类成为基类或派生类的能力无关。两者都是合法的。
但是,使析构函数虚拟化是有某些原因的。请参阅此处:http://en.wikipedia.org/wiki/Virtual_destructor#Virtual_destructors。这使得一个类(除其他外)有一个虚拟表(如果还没有虚拟表)。但是,C++ 不需要虚拟表来执行继承。
评论
是否有任何情况可以使派生 类有一个非虚拟析构函数?
是的。
非虚拟析构函数表示类不应用作 基类。
没有;非虚拟析构函数表示删除 via a 指针的实例将不起作用。例如:derived
base
class Base {};
class Derived : public Base {};
Base* b = new Derived;
delete b; // Does not call Derived's destructor!
如果你不以上述方式做,那就没问题了。但如果是这样的话,那么你可能会使用组合而不是继承。delete
让派生类的非虚拟析构函数像 Java final 修饰符的弱形式?
否,因为 -ness 会传播到派生类。virtual
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++11 标准的摘录,正式定义了这一点:
[...]如果一个类具有具有虚拟析构函数的基类,则其析构函数(无论是用户声明的还是隐式声明的)是虚拟的。
C++03 或更早版本中没有内置的语言机制来防止子类 (*)。无论如何,这并不是什么大问题,因为您应该始终更喜欢组合而不是继承。也就是说,当“is-a”关系比真正的“has-a”关系更有意义时,使用继承。
(*)C++ 11 中引入了“final”修饰符
评论
Base *b = new Derived(); delete b;
b
Derived
delete
new
非虚拟析构函数是完全可以的,只要您不想在删除对象时将其用作派生类的基指针。
如果你以多态的方式派生类,用基指针传递和存储它,然后删除它,那么答案是否定的,使用虚拟析构函数。
评论
如果从不对指向派生类对象的基类指针调用 delete,那么具有非虚拟析构函数的基类是完全有效的。
准则#:仅当派生类需要调用虚函数的基本实现时,才使虚函数受到保护。 仅适用于析构函数的特殊情况:
准则#:基类析构函数应为公共和虚拟,或受保护和非虚拟。
也许您的问题实际上是:
如果基类析构函数是虚拟的,那么派生类中的析构函数是否需要是虚拟的?
答案是否定的。如果基类析构函数是虚拟的,则派生类析构函数已经是隐式虚拟的,您不需要显式地将其指定为虚拟。
评论
取决于您的课程目的。有时,一个好的做法是让你的析构函数受到保护,但不是虚拟的——这基本上是说:“你不能通过基类型指针删除派生类的对象”
是的,有:
void dothis(Base const&);
void foo() {
Derived d;
tothis(d);
}
这里该类是多态使用的,但未被调用,因此很好。delete
另一个例子是:
std::shared_ptr<Base> create() { return std::shared_ptr<Base>(new Derived); }
因为 a 能够使用非多态(通过类型擦除)。shared_ptr
delete
我在 Clang 中实现了一个警告,专门用于检测使用非虚拟析构函数对多态非最终类的调用,因此如果您使用 ,它将专门针对这种情况发出警告。delete
clang -Wdelete-non-virtual-dtor
如果派生类没有向基类添加任何数据成员,并且析构函数体为空,则析构函数是否为虚拟并不重要 - 派生的析构函数将要做的就是调用基类。不建议这样做,因为有人在不知道这些限制的情况下很容易出现并修改类。
如果从不尝试通过指向基类的指针删除对象,则可以安全。这是另一个难以执行的规则,应谨慎使用。
有时你对基类没有任何控制权,你被迫从它派生,即使析构函数不是虚拟的。
最后,在基类中有一个非虚拟析构函数不会对编译器强制执行的派生类施加任何限制,所以我认为它根本不像 Java 的最终版本。
评论
你的问题不是很清楚。如果基类具有虚拟 析构函数,无论如何,派生类都会有一个。没办法 在声明虚拟性后将其关闭。
当然,在某些情况下,从
没有虚拟析构函数的类。基地的原因
类析构函数应该是虚拟的,这样你就可以通过
指向基类的指针。如果派生是私有的,则没有
担心这一点,因为您不会转换为.
否则,我已经看到建议,如果基类
析构函数不是虚拟的,它应该受到保护;这可以防止一个
未定义行为(通过指向基的指针删除)的情况
可能发生。然而,在实践中,许多基类(例如)具有语义,因此它甚至不会发生
任何人创建指向它们的指针;更不用说通过这样的删除了
指针。因此,添加保护可能比它的价值更费力。Derived*
Base*
std::iterator<>
评论
解决最新编辑:
编辑:我对派生类的基类具有虚拟析构函数的情况特别感兴趣。
在这种情况下,派生类的析构函数将是虚拟的,无论是否添加关键字: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()
拥有派生类的非虚拟析构函数会像 Java 最终修饰符的弱形式一样吗?
一点也不。这是我的建议,以防止C++中的子类(如Java中的最终修饰符);在类中将析构函数设为私有。然后,您可以阻止从中创建子类
您可能不想在基类中创建虚拟析构函数?在这种情况下,没有析构函数执行任何操作。如果使用指向基类的指针并在父级中创建非虚拟析构函数,则编译器会自动生成此警告!如果要创建最终的父类,可以阻止它。但最好将其标记为最终喜欢:
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
};
这是编译器的完整清晰代码。不会生成警告,也不会使用备用代码。
评论