开发者

Are there cases where a class declares virtual methods and the compiler does not need to use a vptr?

I was wondering if there is a possible optimization where the compiler does not need to assign a vptr to an instantiated object even though the object's type is a class with virtual methods.

For example consider:

#include <iostream>
struct FooBase
{
  virtual void bar()=0;
};

s开发者_如何学运维truct FooDerived : public FooBase
{
  virtual void bar() { std::cout << "FooDerived::bar()\n"; }
};

int main()
{
   FooBase* pFoo = new FooDerived();
   pFoo->bar();

  return 0;
}

In this example the compiler surely knows what will be the type of pFoo at compile time, so it does not need to use a vptr for pFoo, right? Are there more interesting cases where the compiler can avoid using a vptr?


Building on Andrew Stein's answer, because I think you also want to know when the so-called "runtime overhead of virtual functions" can be avoided. (The overhead is there, but it's tiny, and rarely worth worrying about.)

It's really hard to avoid the space of the vtable pointer, but the pointer itself can be ignored, including in your example. Because pFoo's initialization is in that scope, the compiler knows that pFoo->bar must mean FooDerived::bar, and doesn't need to check the vtable. There are also several caching techniques to avoid multiple vtable lookups, ranging from the simple to the complex.


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.


Even the most modern, globally-optimizing compilers are still sticking to the principle of "independent translation". According to this principle, every translation unit is compiled independently, without any knowledge about any other translation units that might exist in the entire final program.

When you declare a class with external linkage, this class can be used in other translation units. The compiler has no idea about how your class might be used in those other translation units. So, it has to assume that every instance of that class might in general need its virtual table pointer initialized properly at the moment of construction.

In your example a smart compiler might figure out that the dynamic type of *pFoo object is FooDerived. That would allow the compiler to optimize the code: to generate a direct call to FooDerived::bar function in response to pFoo->bar() expression (without using the virtual table). But nevertheless, the compiler normally would still have to properly initialize the virtual table pointer in each FooDerived object.


It is possible for a compiler to cut out the indirection overhead of virtual function calls when it knows which exact class's method is going to be invoked. This is the result of a form of points-to analysis that optimizing compilers do. They trace the data flow of pointer variables, and any place a pointer can only point to objects of one type, it can generate calls for exactly that type, implementing virtual call semantics statically (i.e. at compile time).

As many others have said, unless the compiler is doing whole-program/link-time optimization (becoming more popular; GCC gains it in 4.5), it still needs to generate some sort of table of virtual functions to support separate compilation.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜