为什么虚拟析构函数需要删除运算符
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 中现在有一个修复:P0722R3。 static 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
)
在独立上下文中(没有标准库,例如在操作系统开发中)使用 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 中现在有一个修复:P0722R3。 static 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
)