非多态基类中的析构函数
Destructors in non-polymorphic base classes
请考虑以下代码:
class A {
public:
void fun() {}
};
class B : public A {
public:
void fun() {}
};
int main()
{
A *p = new B;
delete p;
}
类 A 和 B 不是多态的,并且两个类都不声明析构函数。如果我用g++ -Wall
编译这段代码,GCC编译器会愉快地编译代码。
但是,如果我在 A 中void fun()
中添加virtual
,编译器会发出此警告:"删除具有非虚拟析构函数的多态类类型'A'的对象可能会导致未定义的行为"。
我非常清楚使用非虚拟析构函数的危险。但是上面的代码让我想知道两件事:
- 当我根本不使用析构函数时,为什么需要在基类中编写一个空的虚拟析构函数?
- 如果基类不包含其他虚函数,为什么不需要空的虚拟析构函数?
编辑
看来我需要澄清困扰我的事情:
上面的代码没有声明析构函数。
如果我声明一个虚拟函数,编译器会抱怨缺少虚拟析构函数。我的结论:如果类是多态的,如果要正常工作delete p
我需要编写一个虚拟析构函数。
但是,如果我声明没有虚函数(如上面的初始示例),编译器不会抱怨缺少虚拟析构函数。我的结论是:如果类不是多态的,我不需要编写虚拟解构器,无论如何delete p
都可以正常工作。
但最后这个结论对我来说在直觉上听起来是错误的。有错吗?编译器应该在这两种情况下都抱怨吗?
继PaulMcKenzie和KerrekSB的评论之后,以下是原始帖子中两个问题的答案:
- 该类始终有一个析构函数,即使程序员没有显式编写析构函数也是如此。有必要声明一个空的虚拟析构函数,以防止系统自动生成非虚拟析构函数。
- 在提供的示例代码中,您确实需要一个虚拟析构函数,即使类中没有其他虚拟函数也是如此。在这种情况下,GCC不抱怨的事实可能是编译器中的一个错误(或者至少是一个缺点)。
C++11 标准的 §5.3.5 中可以找到这方面的背景,其中说:"如果要删除的对象的静态类型与其动态类型不同,则静态类型应是要删除的对象的动态类型的基类,静态类型应具有虚拟析构函数或行为未定义。(斜体是我的。
你正在进行一个向上转换,换句话说:对类 B 的多态使用。如果类 A 没有虚拟成员,则编译器不会为类 A 生成 VTABLE,并且不需要虚拟析构函数(请注意,如果不使用多态性,则向上转换毫无意义)。虽然类 A 声明虚拟成员,则 VTABLE 由编译器生成,在这种情况下,应提供虚拟析构函数。
如果你想要多态行为,你需要定义至少一个虚函数,以便编译器应该为你的类生成v-table。
因为类包含两个特殊的函数(构造函数和析构函数C++所以每个对象都使用它作为使析构函数虚拟的不错选择。
-
当你写
delete p
时,你实际上是为与指针p
关联的对象调用析构函数。如果不将析构函数声明为虚拟,则会得到容易出错的代码。 -
在将至少一个成员函数声明为虚拟编译器之前,您并不打算将类用作多态。在C++系统学中:"你不应该为你永远不会使用的功能付费"。例如,在简单的情况下,析构函数不应该是虚拟的。
- 使用基类指针创建对象时,缺少派生类析构函数
- 对具有动态分配的内存和析构函数的类对象的引用
- 基类中的默认析构函数禁用子类中的移动构造函数(如果有成员)
- 即使基类和派生类只使用基元数据类型,我是否需要定义虚拟析构函数
- C++11:我可以显式调用基类析构函数来销毁派生类吗
- 为什么我不能使用已删除或私有析构函数分配类的数组?
- 显式默认析构函数禁用类中的默认移动构造函数
- 具有受保护析构函数的类数组的动态分配
- 不带名称、构造函数或析构函数的类
- 我可以从析构函数的类方法调用它吗
- 否,除非函数返回具有抛出析构函数的类
- 析构函数子类中的 c++ 分段错误
- 从没有虚拟析构函数的类继承
- 非平凡的析构函数使类具有非平凡的可构造性
- 是否应该初始化具有未重写的纯虚拟析构函数的类
- 为什么带有用户定义析构函数的类的实例将成员指针设置为零
- 为什么不能对带有析构函数的类进行忆及
- 为什么不为定义了析构函数的类合成move操作呢?
- 在析构函数中将类成员设置为空
- 在第三方软件中派生没有虚析构函数的类