我正在开发一个不使用动态内存分配的嵌入式系统程序。如何防止 GCC 生成 deleting destructor(名称中带有
D0
的析构函数)?它永远不会被调用。
我认为甚至不需要删除析构函数,因为可以调用完整的对象析构函数(在损坏的名称中为
D1
)以及其后的operator delete(…)
。
在我看来,应该有一些命令行选项来禁用这些析构函数。
主要问题是程序在删除析构函数中调用了
operator delete(…)
。由于我没有任何堆管理,因此没有定义这样的函数。作为一种解决方法,我可以实现仅报告错误的 operator delete(…)
,但这会浪费内存(不必要的大程序大小),并且不允许我在编译期间捕获对 operator delete()
的意外调用。
存在删除析构函数变体,因此语法
delete ptr;
其中
ptr
指向多态类型,即使 ptr
不指向最派生的对象,也可以工作。 delete ptr;
的用户不知道最衍生对象的偏移量或其大小是多少,但需要知道这一点才能正确调用 operator delete
。因此,需要对知道的函数进行间接/虚拟调用,即删除析构函数。
不幸的是,编译器必须至少为所有用作最派生对象的类从这样的
virtual
析构函数生成删除析构函数,因为在另一个翻译单元中可能存在这种类型的 delete
表达式。不知道这个最派生的析构函数的定义,但是它必须(间接)调用它。
我不认为你可以将虚拟析构函数的行为与执行显式虚拟析构函数调用的能力分开,而且我也没有看到任何 GCC 开关来禁用删除析构函数的生成作为非标准/符合 ABI 的选项。
所以我想你必须避免虚拟析构函数。无论如何,您都可以通过从delete
函数转发来获得虚拟销毁行为:
virtual
然后您可以使用
struct Base {
virtual destroy() noexcept { this->~Base(); }
~Base(); // Not virtual
};
struct Derived {
virtual destroy() noexcept override { this->~Derived(); }
};
代替
ptr->~Base();
/std::destroy_at(ptr)
。但是,这存在一个问题,您需要确保在每个派生类中正确重写 ptr->destroy()
。 CRTP 或 C++23 显式对象参数(显式
destruct
)可能会有所帮助。
或者,我建议在每个翻译单元中将 this
定义为内联和 noop。这样,GCC 似乎至少将其内联到删除析构函数中。