使派生的C ++类“最终”会改变ABI吗?

时间:2018-11-20 07:10:23

标签: c++ c++11 virtual-functions abi vtable

我很好奇,如果在使用C ++ 11时将现有的派生C ++类标记为final以进行去虚拟化优化,是否会改变ABI。我的期望是它不起作用,因为我认为这主要是向编译器提示如何优化虚拟函数,因此我看不到它将改变struct或vtable大小的任何方式,但是也许我想念什么?

我知道这里会更改API,因此从该派生类进一步派生的代码将不再起作用,但在这种情况下,我只关心ABI。

2 个答案:

答案 0 :(得分:0)

我认为,添加final关键字不会破坏ABI,但是从现有类中删除它可能会使某些优化无效。例如,考虑一下:

// in car.h
struct Vehicle { virtual void honk() { } };
struct Car final : Vehicle { void honk() override { } };

// in car.cpp

// Here, the compiler can assume that no derived class of Car can be passed,
// and so `honk()` can be devirtualized. However, if Car is not final
// anymore, this optimization is invalid.
void foo(Car* car) { car->honk(); }

如果foo是单独编译的,例如运送到共享库中,删除final(从而使用户有可能从Car派生)可能会使优化无效。

尽管我不确定100%,但其中有些是猜测。

答案 1 :(得分:0)

如果您没有在final类中引入新的虚拟方法(仅覆盖父类的方法),则应该没问题(虚拟表将与父对象相同,因为它必须是可以使用指向父对象的指针来调用),如果您引入虚拟方法,则编译器确实可以忽略virtual说明符,而仅生成标准方法,例如:

class A {
    virtual void f();
};

class B final : public A {
    virtual void f(); // <- should be ok
    virtual void g(); // <- not ok
};

这个想法是,每次在C ++中您可以调用方法g()时,都有一个指针/引用,其静态和动态类型为B:静态,因为除{ {1}}和他的孩子们充满活力,因为B确保final没有孩子。因此,您无需进行虚拟调度即可调用 right B实现(因为只能有一个),并且编译器可能(并且不应)将其添加到虚拟环境中。 g()的表格-如果可以覆盖该方法,则必须这样做。据我所知,这基本上就是B关键字存在的重点