2012-09-22 86 views
3

我有一个关于虚拟函数调用优化的问题。我已阅读的地方(而问题是,现在我无法找到文章),这可能是通过使用一个类似的结构来优化掉V-查表:虚拟函数调用的优化

// Base.h 
class Base 
{ 
public: 
    virtual void Foo() = 0; 
}; 

// Concrete.h 
class Concrete : public Base 
{ 
public: 
    virtual void Foo() 
    { 
     // do something; 
    } 
}; 

//Some.h 
extern Base* const g_object; 

// Some.cpp 
Concrete on_stack_concrete; 

Base* const g_object = &on_stack_concrete; 

诀窍应该使用一个const指针指向一个在堆栈上分配的变量(不是动态的),编译器肯定会优化它。因此,无论用户何时调用g_object-> Foo(),都可以执行部分​​操作,而无需v-table查找。

这是真的吗?

预先感谢任何重播。

编辑:

这种构建体的可能的使用是限制具体实现的接口。当然,人们可以争辩说,“受限制”的方法应该是私有的,但有时图书馆的其他模块需要访问对象的公共附加方法,而不允许用户操纵这些方法。因此,例如使用#定义,我们可以创建类似的代码:因此

// Some.cpp 
#ifdef _WIN32 
Win32Concrete concrete; 
#elif defined _UNIX 
UnixConcrete concrete; 
#endif 

Base* const g_global = &concrete; 

事实上这些类的声明只能在CPP文件中定义,用户不知道它们的存在。

问题不是为什么首先使用这样的常量指针,但是如果可以在这种情况下优化掉v-table查找。

+2

这可能是编译器相关的,你应该能够验证这看你的编译器生成的程序集 – marcinj

+2

为什么使用指针,如果它总是指向同一个对象? –

+0

@BoPersson,我现在可以想到一个好的应用程序,例如跨平台库,仅向用户公开抽象接口,而后端根据某些#defines针对各种平台使用多个实现。这当然需要对具有不同定义集合的每个平台进行多次编译,但对于最终用户来说,这没有什么不同。 –

回答

4

您好像被误用了virtual

virtual实现运行时多态性。你描述的场景并不使用或不需要。在任何编译环境中都不可能存在Win32ConcreteUnixConcrete

相反的:

// Some.cpp 
#ifdef _WIN32 
Win32Concrete concrete; 
#elif defined _UNIX 
UnixConcrete concrete; 
#endif 

Base* const g_global = &concrete; 

使用:

// CommonHeader.h 
#ifdef _WIN32 
typedef Win32Concrete Concrete; 
#elif defined _UNIX 
typedef UnixConcrete Concrete; 
#endif 

现在你的功能并不需要是虚的。

+0

这种方式初始化全局变量的一点是避免将它作为参数传递给函数,因此这种情况就不会发生。它是全球性的,因此它在任何地方都可以访问,所以你所提到的“概念”在我的理解中不会改变。 –

+0

@AdrianLis如果你直接访问全局变量,那么为什么要创建'Base * const g_object'呢?你可以使用'on_stack_concrete.foo()'代替。在这种情况下,您可以避免查找。 –

+0

因为具体类可以向用户展示他不应该直接使用的不同(额外)接口。这就是为什么指向基类的指针是为了将界面仅限于重要的东西。我将编辑问题以显示可能的用法。 –

1

解决此问题的最简单方法是使需要访问Concrete类的受限方法朋友的类。然后他们可以完全访问课程,其他人只能获得公共访问权限。

如果这样做不可行,可以将实现放在基类中,并且保护所有受限制的方法,然后派生出一个公开受保护方法的特殊类。

class Concrete 
{ 
public: 
    void foo() { ... } 
protected: 
    void bar() { ... } 
}; 

class ConcretePrivate : public Concrete 
{ 
public: 
    void bar() { Concrete:: bar(); } 
}; 

ConcretePrivate g_globalPrivate; 
Concrete& g_global = g_globalPrivate; 

使用g_global的代码只能访问具体的方法。使用g_globalPrivate的代码可以访问ConcretePrivate方法。

1

这是在Doom 3源代码(https://github.com/id-Software/DOOM-3-BFG/)中使用的方法,例如neo/framework/FileSystem。^ h定义如下:

extern idFileSystem *  fileSystem; 

和新/框架/ FileSystem.cpp定义是:

idFileSystemLocal fileSystemLocal; 
idFileSystem *  fileSystem = &fileSystemLocal; 

我能找到关于它的唯一讨论的是:http://fabiensanglard.net/doom3/

idTech4高水平对象都是具有虚拟方法的抽象类。这通常会涉及性能问题,因为每个虚拟方法地址在运行时调用它之前都必须在vtable中查找。但是有一个“窍门”可以避免这种情况。

由于在数据段中静态分配的对象具有已知类型,因此编译器可以在调用commonLocal方法时优化掉vtable查找。