我想知道是否存在可能的优化,即使对象的类型是具有虚方法的类,编译器也不需要将vptr分配给实例化对象。
例如考虑:
#include <iostream>
struct FooBase
{
virtual void bar()=0;
};
struct FooDerived : public FooBase
{
virtual void bar() { std::cout << "FooDerived::bar()\n"; }
};
int main()
{
FooBase* pFoo = new FooDerived();
pFoo->bar();
return 0;
}
在这个例子中,编译器肯定知道在编译时pFoo的类型是什么,所以它不需要为pFoo使用vptr,对吧? 是否有更有趣的情况,编译器可以避免使用vptr?
答案 0 :(得分:4)
即使在你展示的情况下,我怀疑任何编译器都会按照你的建议行事。
您在一个文件中使用一个非常简单的程序。
想象一下,你在Foo.h和Foo.cpp中使用了FooBase和FooDerived,在main.cpp中使用了main。在编译Foo.cpp时,编译器如何知道在整个程序中不需要vtbl。它没有见过main.cpp。
最终的决定只能在链接时进行,当修改目标文件,找到所有对sizeof(FooDerived)等的调用时,为时已晚,而且很复杂。
答案 1 :(得分:4)
以Andrew Stein's answer为基础,因为我想你也想知道什么时候可以避免所谓的“虚函数的运行时开销”。 (开销是存在的,但是很小,很少值得担心。)
很难避免vtable指针的 space ,但指针本身可以被忽略,包括在你的例子中。因为 pFoo 的初始化在该范围内,所以编译器知道pFoo->bar
必须表示 FooDerived :: bar ,并且不需要检查vtable。还有一些缓存技术可以避免多个vtable查找,范围从简单到复杂。
答案 2 :(得分:0)
即使是最现代化,全球优化的编译器仍然坚持“独立翻译”的原则。根据这一原则,每个翻译单元都是独立编译的,不需要了解整个最终程序中可能存在的任何其他翻译单元。
当您声明具有外部链接的类时,此类可以在其他翻译单元中使用。编译器不知道如何在其他翻译单元中使用您的类。因此,它必须假设该类的每个实例通常都需要在构造时正确初始化其虚拟表指针。
在您的示例中,智能编译器可能会发现*pFoo
对象的动态类型为FooDerived
。这将允许编译器优化代码:生成对FooDerived::bar
函数的直接调用以响应pFoo->bar()
表达式(不使用虚拟表)。但是,编译器通常仍需要正确初始化每个FooDerived
对象中的虚拟表指针。
答案 3 :(得分:0)
当编译器知道要调用哪个确切类的方法时,编译器可以切断虚函数调用的间接开销。这是优化编译器的 point-to 分析形式的结果。它们跟踪指针变量的数据流,并且指针只能指向一种类型的对象,它可以生成完全相同类型的调用,静态地实现虚拟调用语义(即在编译时)。
正如许多其他人所说,除非编译器正在进行整个程序/链接时优化(变得越来越流行; GCC在4.5中获得它),它仍然需要生成某种虚函数表来支持单独的编译