使用虚函数销毁堆栈对象与删除非最终 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总是正确的,不需要警告。