0

我想知道是否有可能的优化,即使对象的类型是具有虚拟方法的类,编译器也不需要将 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?

4

4 回答 4

4

Even in the case you show, I doubt that any compiler will do what you suggest.

You are using a very simple program all in one file.

Imagine that you had FooBase and FooDerived in Foo.h and Foo.cpp and main in main.cpp. When compiling Foo.cpp, how is the compiler to know that in the whole program there is no need for a vtbl. It has not seen main.cpp.

The final determination can only be made at link time, when it is way too late and complicated to modify the object file, find all the calls to sizeof(FooDerived), etc.

于 2010-02-25T17:32:17.027 回答
4

基于Andrew Stein 的回答,因为我认为您还想知道什么时候可以避免所谓的“虚拟函数的运行时开销”。(开销是存在的,但它很小,很少值得担心。)

确实很难避免 vtable 指针的空间,但是可以忽略指针本身,包括在您的示例中。因为pFoo的初始化在那个范围内,编译器知道它pFoo->bar必须意味着FooDerived::bar,并且不需要检查 vtable。还有几种缓存技术可以避免多个 vtable 查找,范围从简单到复杂。

于 2010-02-25T17:52:20.993 回答
0

即使是最现代的、全局优化的编译器,仍然坚持“独立翻译”的原则。根据这个原则,每个翻译单元都是独立编译的,不知道整个最终程序中可​​能存在的任何其他翻译单元。

当您声明具有外部链接的类时,该类可以在其他翻译单元中使用。编译器不知道您的类如何在其他翻译单元中使用。因此,它必须假设该类的每个实例通常可能需要在构造时正确初始化其虚拟表指针。

在您的示例中,智能编译器可能会发现*pFoo对象的动态类型是FooDerived. 这将允许编译器优化代码:生成对FooDerived::bar函数的直接调用以响应pFoo->bar()表达式(不使用虚拟表)。但是尽管如此,编译器通常仍必须正确初始化每个FooDerived对象中的虚拟表指针。

于 2010-02-25T18:05:46.197 回答
0

当编译器知道要调用哪个确切类的方法时,它可以减少虚函数调用的间接开销。这是优化编译器所做的一种指向分析形式的结果。它们跟踪指针变量的数据流,并且指针只能指向一种类型的对象的任何地方,它都可以生成针对该类型的调用,静态地实现虚拟调用语义(即在编译时)。

正如许多其他人所说,除非编译器正在执行整个程序/链接时优化(变得更流行;GCC 在 4.5 中获得它),否则它仍然需要生成某种虚函数表以支持单独编译。

于 2010-02-25T18:21:38.080 回答