gcc 禁止生成删除析构函数

g0czyy6m  于 2023-10-19  发布在  其他
关注(0)|答案(1)|浏览(125)

我正在开发一个程序的嵌入式系统,将不使用动态内存分配。如何防止GCC生成 *deleting析构函数 *(名称中有D0的析构函数)?它永远不会被召唤。
我认为甚至不需要删除析构函数,因为 *complete object析构函数 *(名称中的D1)可以沿着operator delete(…)一起调用。
在我看来,应该有一些命令行选项来禁用这些析构函数。
主要问题是程序在删除析构函数中调用operator delete(…)。因为我没有任何堆管理,所以没有定义这样的函数。作为一种变通方法,我可以实现只报告错误的operator delete(…),但这会浪费内存(不必要的大程序大小),并且不允许我在编译期间捕获对operator delete()的意外调用。

uqjltbpv

uqjltbpv1#

删除析构函数变量的存在使得语法

delete ptr;

其中ptr指向多态类型可以工作,即使ptr没有指向最派生的对象。delete ptr;的用户不知道最派生的对象的偏移量或其大小是多少,但为了正确调用operator delete,它需要知道这一点。因此,需要对一个知道的函数进行间接/虚拟调用,即删除析构函数。
不幸的是,编译器必须从这样的virtual析构函数中生成删除析构函数,至少对于所有用作最派生对象的类,因为在另一个翻译单元中可能存在这种类型的delete表达式,它不知道这个最派生析构函数的定义,但它必须(间接)调用。
我不认为你可以将虚拟析构函数的delete行为与执行显式虚拟析构函数调用的能力分开,我也没有看到任何GCC开关来禁用作为非标准/ABI兼容选项的删除析构函数的生成。
所以我想你必须避免使用虚拟析构函数。你可以通过从virtual函数转发来获得虚拟销毁行为:

struct Base {
    virtual destroy() noexcept { this->~Base(); }
    // destructor not virtual
};

struct Derived {
    virtual destroy() noexcept override { this->~Derived(); }
};

然后可以使用ptr->destroy()来代替ptr->~Base();/std::destroy_at(ptr)
然而,这有一个问题,你需要确保destruct在每个派生类中都被正确地重写,以避免未定义的行为。CRTP或C++23显式对象参数(显式this)可能会有所帮助。
还有一个问题是,有人可能意外地直接调用析构函数,再次导致未定义的行为。创建析构函数private通常也不是一个解决方案,因为析构函数在许多情况下都可能被调用,例如。在一个类的构造函数中,该构造函数包含有问题的类作为非静态成员。
我最初在这里建议将operator delete实现为空,并在每个转换单元中实现inline以帮助删除调用,但我没有意识到将可替换的释放函数标记为inline会使程序IFNDR无效。(参见[替换功能]/3。)
你不能删除operator delete,因为它将被odr使用,因为我上面提到的原因,因此必须可行。
LTO可能会进一步帮助在链接过程中摆脱未使用的删除析构函数,至少通过去虚拟化。但我还没测试过。

相关问题