虚拟析构函数将对象移出rodata部分

Rob*_* B. 12 c++ gcc elf

我有大量使用constexpr构造函数构造的静态常量对象,因此它们无需任何构造函数调用即可立即存储在最终二进制文件中。

由于我正在使用内存较低的系统(STM32 MCU),因此我想减少这些对象的内存占用,并且由于它们是恒定的,因此请将其存储在该.rodata部分中。编译器对此进行了顺利处理。

但是,既然我已在基类中添加了虚拟析构函数以删除编译器警告,则对象将存储在该.data部分中。

当然,我可以使用某些方法#pragma专门删除基类的编译器警告,并删除虚拟析构函数,但是我想知道是否有更干净的解决方案。

极简主义代码展示了这个问题:

class Object {
    int value;
public:
    constexpr Object(int param) 
    : value(param) {}

    virtual int getValue() const = 0;

    virtual ~Object() = default; // This line causes problems
};

class Derived : public Object {
    volatile int otherValue;
public:
    constexpr Derived(int param1, int param2) 
    : Object(param1), otherValue(param2) {}

    int getValue() const override { return otherValue; }
};


const Derived instance(1,2);

int main() {
    return instance.getValue();
}
Run Code Online (Sandbox Code Playgroud)

另外,这是一个CompilerExplorer,可以在有和没有虚拟析构函数的情况下进行比较:https : //godbolt.org/z/M5G7LO

Shl*_*oim 9

声明虚拟方法后,便向该类添加了一个非恒定指针,该指针指向该类的虚拟表。该指针将首先被初始化为Object的虚拟表,然后在整个构造函数链中继续更改为派生类的虚拟指针。然后,它将在析构函数链中再次更改并回滚,直到指向对象的虚拟表为止。这意味着您的对象不再可以是纯只读对象,而必须移出.rodata。

较干净的解决方案是忽略类中的任何虚函数,或者完全避免继承,并使用模板将所需的虚函数调用替换为编译时调用。