3

我有一个关于优化虚函数调用的问题。我在某处读过(问题是我现在找不到这篇文章),可以通过使用类似于以下的构造来优化 v-table 查找:

// 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() 时, //do something 部分将被执行,而无需查找 v-table。

这是真的还是假的?

提前感谢任何重播。

编辑:

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

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

Base* const g_global = &concrete;

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

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

4

3 回答 3

4

你似乎在滥用virtual.

virtual实现运行时多态性。而且您描述的场景不需要或不需要。Win32Concrete两者都不太可能UnixConcrete存在于任何编译环境中。

代替:

// 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

现在你的函数不需要是虚拟的。

于 2012-09-22T12:54:12.847 回答
2

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

extern idFileSystem *       fileSystem;

neo/framework/FileSystem.cpp 定义了这个:

idFileSystemLocal   fileSystemLocal;
idFileSystem *      fileSystem = &fileSystemLocal;

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

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

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

于 2016-10-21T08:28:12.203 回答
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 方法。

于 2012-09-22T13:32:13.847 回答