虚函数的最终奇数行为

时间:2015-05-18 23:30:05

标签: c++ final c++14 undefined-reference

final关键字添加到虚拟函数声明中时,我遇到了一个奇怪的情况,其定义在一个单独的.cpp文件中。
请考虑以下示例:

IClass.hpp

class IClass //COM-like base interface
{
protected:
    virtual ~IClass(){} //derived classes override this

public:
    virtual void release() final;
};

dllmain.cpp (共享库)

#include "IClass.hpp"
...

void IClass::release()
{
    delete this;
}

...

main.cpp (独立可执行文件)

//various includes here
...

int main(int argc, char** argv)
{
    /* From "IGameEngine.hpp"
       class IGameEngine : public IClass
       {
       ...
       };
    */
    IGameEngine* engine = factoryGameEngine();
    ...
    engine->release();
    return 0;
}

实际上,GCC 4.9.2将报告undefined reference to 'IClass::release()' 我的目标是让IClass::release()成为不可覆​​盖的,同时将其实现隐藏在游戏引擎的共享库中。
有什么建议吗?

1 个答案:

答案 0 :(得分:2)

对GCC使用final进行了一些挖掘,结果发现虚拟函数标记为final "devirtualized",这是一个优化步骤,旨在通过使用静态调度加速虚拟调用,并可能内联它们。

这解释了链接器错误,因为它试图将IClass::release()链接到可执行文件中但在本地找不到它。

这种“虚拟化”行为也出现在clang上,但不太可能发生在MSVC ++上

部分相关的建议

  

如果您需要通过指向其抽象类(或抽象基类)的指针释放对象的方法:

  • 抽象基类需要一个纯虚拟析构函数
  • 在类(空范围)
  • 之外提供析构函数的默认定义
  • 像往常一样在所有派生类上实现析构函数

  • 如果您还在处理共享库:

  • 从库中导出一对Malloc / Free函数
  • 覆盖图书馆头文件中的非数组新/删除运算符及其各自的std::nothrow版本
  • 从覆盖操作符
  • 中调用上述Malloc / Free
    由于接口实现将驻留在库中,因此为您认为是客户端可构造的每个接口导出工厂函数。 只要确保异常不会通过客户端和库之间的差距传播。

    这样,客户端应用程序可以对库的CRT分配的对象使用delete,没有麻烦。