为什么虚拟析构函数需要删除运算符

Why is delete operator required for virtual destructors

在独立上下文中(没有标准库,例如在操作系统开发中)使用 g++ 会出现以下现象:

class Base {
public:
   virtual ~Base() {}
};

class Derived : public Base {
public:
    ~Derived() {}
};

int main() {
    Derived d;
}

链接时,它是这样写的:undefined reference to operator delete(void*)

这显然意味着即使动态内存分配为零,g++ 也会生成对 delete 运算符的调用。如果析构函数不是虚拟的,则不会发生这种情况。

我怀疑这与为 class 生成的 vtable 有关,但我不完全确定。 为什么会这样?

如果由于缺少动态内存分配例程而不能声明删除运算符,是否有解决方法?

编辑 1:

为了在 g++ 5.1 中成功重现问题,我使用了:

g++ -ffreestanding -nostdlib foo.cpp

因为删除了析构函数。这些是当您在具有虚拟析构函数的对象上调用 delete obj 时实际调用的函数。它调用完整的对象析构函数(它链接基础对象析构函数——您实际定义的析构函数),然后调用 operator delete。这样一来,在所有使用 delete obj 的地方,只需要发出一次调用,并且还用于根据需要使用从 operator new 返回的相同指针调用 operator delete通过 ISO C++(尽管这也可以通过 dynamic_cast 来完成,成本更高)。

它是 GCC 使用的 Itanium ABI 的一部分。

我认为你不能禁用它。

在 C++20 中现在有一个修复:P0722R3static void operator delete(T*, std::destroying_delete_t) 释放函数。它本质上映射到破坏性析构函数。

你可以让它不调用 ::operator delete,比如:

class Base {
public:
    void operator delete(Base* p, std::destroying_delete_t) {
        // Shouldn't ever call this function
        std::terminate();  // Or whatever abort-like function you have on your platform

        // The default implemenation without any overrides basically looks like:
        // p->~Base(); ::operator delete(p);
        // Which is why the call to `operator delete` is generated
    }
    virtual ~Base() {}
};

class Derived : public Base {
public:
    // Calls Base::operator delete in deleting destructor, so no changes needed
    ~Derived() {}
};

int main() {
    Derived d;
}

删除析构函数是您执行 delete ptr_to_obj; 时调用的析构函数。它只能由 delete 表达式调用,所以如果你的代码中有 none,这应该没问题。如果你这样做,你可以用 ::delete ptr_to_obj; 替换它们并且删除析构函数将不再被调用(它的目的是为 类 和 ::delete 调用覆盖的 operator delete只会调用全局 ::operator delete)