使用虚函数销毁堆栈对象与删除非最终 class 的堆分配对象
Destructing stack object vs deleting heap-allocated object of non-final class with virtual functions
假设您有一个带有虚函数和非虚析构函数的派生 class,例如:
class Base
{
public:
~Base() {}
};
class Derived : public Base
{
public:
Derived() {}
virtual void foo() {}
};
假设您创建了一个 Derived
class 的堆分配对象并使用 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
class'的析构函数是在main
函数的末尾调用的,但是为什么在这种情况下没有错误?
当对象具有自动存储持续时间或者是class的成员时,不需要考虑多态性。在给定的代码中,左值 d
不能引用更派生的对象。因此,调用Derived::~Derived
总是正确的,不需要警告。
假设您有一个带有虚函数和非虚析构函数的派生 class,例如:
class Base
{
public:
~Base() {}
};
class Derived : public Base
{
public:
Derived() {}
virtual void foo() {}
};
假设您创建了一个 Derived
class 的堆分配对象并使用 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
class'的析构函数是在main
函数的末尾调用的,但是为什么在这种情况下没有错误?
当对象具有自动存储持续时间或者是class的成员时,不需要考虑多态性。在给定的代码中,左值 d
不能引用更派生的对象。因此,调用Derived::~Derived
总是正确的,不需要警告。