【发布时间】:2025-12-12 11:05:02
【问题描述】:
假设您有一个带有虚函数和非虚析构函数的派生类,例如:
class Base
{
public:
~Base() {}
};
class Derived : public Base
{
public:
Derived() {}
virtual void foo() {}
};
假设您创建了一个Derived 类的堆分配对象并使用delete 关键字将其删除,例如:
int main()
{
Derived *d = new Derived();
delete d;
}
使用-Wall -Wdelete-non-virtual-dtor -Werror 标志编译此代码会抛出一个错误,这完全没问题,因为它最终可能会导致 UB。演示here.
显然,调用d 对象的析构函数是导致编译器错误的原因,因为以下代码具有相同的结果(至少在CLANG 上,GCC 与以下代码没有问题):
int main()
{
Derived d;
d.~Derived();
}
但如果我在堆栈上创建一个简单的对象,CLANG 和 GCC 都不会出现编译器错误:
int main()
{
Derived d;
}
我们都知道Derived类的析构函数是在main函数的最后被调用的,但是为什么在这种情况下没有错误呢?
【问题讨论】:
-
手动调用析构函数并不违法(即使这里不正确),那为什么会产生错误呢?
-
@super 我希望它会产生错误,因为
delete正在产生错误,如果我没记错的话,delete会调用对象的析构函数。但是,可能仅在运行时多态的情况下才会引发错误,并且 CLANG 在手动调用堆栈对象上的析构函数时存在错误。 -
恕我直言,编译器是对的。它不知道 d 是否指向 Derived 的实例,它可以指向 Derived 的派生类的对象。使用关键字 final。
-
在这种情况下这不是错误,如果您尝试删除某些派生的后代,这是一个警告。这段代码不是格式错误的,只是
-Werror阻止它被编译。 -
@S.M.好的,现在我明白了。谢谢
标签: c++ destructor final virtual-functions