【问题标题】:Odd behaviour of final on a virtual functionfinal 在虚函数上的奇怪行为
【发布时间】:2015-07-30 14:50:53
【问题描述】:

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;
}

...

ma​​in.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() 不可覆盖,同时将其实现隐藏在游戏引擎的共享库中。
有什么建议吗?

【问题讨论】:

  • 虚拟函数总是被 odr-used 除非它们是纯的。我相信在这种情况下允许链接器发出错误。
  • 我无法在我的 gcc 4.9.2 上重现该问题。请添加您构建程序的方式(编译器选项等)
  • 你从dll中导出函数了吗? @0x49 我错觉他们的 odr-used-ness 是实现定义的。
  • @dyp: dll 是使用这个makefile 构建的 主二进制文件使用相同的编译器标志,但以下链接器标志:-static-libgcc -static-libstdc++ -mwindows
  • @Yakk [basic.def.odr]/p5:“如果虚拟成员函数不是纯的,则它是 odr-used。”我认为是否存在链接器错误取决于实现。

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


【解决方案1】:

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

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

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


部分相关的建议

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

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

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

  • 从库中导出一对 Malloc/Free 函数
  • 覆盖库头文件中的非数组 new/delete 运算符及其各自的 std::nothrow 版本
  • 从覆盖的运算符中调用上述 Malloc/Free

  • 由于接口实现将驻留在库中,因此为您认为客户端可构造的每个接口导出一个工厂函数。
    只需确保异常不会通过客户端和库之间的间隙传播。

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

    【讨论】:

      猜你喜欢
      • 2015-11-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-24
      • 1970-01-01
      • 2019-06-03
      • 2011-06-14
      • 2020-01-31
      相关资源
      最近更新 更多