我很好奇,如果在使用C ++ 11时将现有的派生C ++类标记为final
以进行去虚拟化优化,是否会改变ABI。我的期望是它不起作用,因为我认为这主要是向编译器提示如何优化虚拟函数,因此我看不到它将改变struct或vtable大小的任何方式,但是也许我想念什么?
我知道这里会更改API,因此从该派生类进一步派生的代码将不再起作用,但在这种情况下,我只关心ABI。
答案 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
关键字存在的重点