类vtable如何跨共享库工作?

时间:2017-01-11 05:46:37

标签: c++ vtable virtual-method one-definition-rule

假设我有一个名为libplugin的共享库。在这个共享库中,有一个类:

class Plugin
{
    public:
        virtual void doStuff();
};

我们还假设有另一个名为libspecialplugin的共享库。它包含以下类和函数:

class SpecialPlugin : public Plugin
{
    public:
        virtual void doStuff();
};

Plugin *createSpecialPlugin()
{
    return new SpecialPlugin;
}

现在,假设我更改Plugin并添加以下方法:

virtual void doMoreStuff();

重新编译libspecialplugin

当我这样做时会发生什么:

Plugin *plugin = createSpecialPlugin();
plugin->doMoreStuff();

我猜测会发生以下情况之一:

  1. 应用程序崩溃
  2. 调用Plugin::doMoreStuff()方法
  3. libspecialplugin库是否包含libplugin可用于确定哪些方法被覆盖的信息 - 即使在运行时?我对这应该发生的事情有点模糊。

2 个答案:

答案 0 :(得分:6)

通过在使用这两个库的任何程序中的两个不同翻译单元中定义相同的类(Plugin),您实际上违反了“一个定义规则”。

标准说(C ++ 11 ISO 14882:2011,§3.2第5段):

  

类类型可以有多个定义(第9条)......   在一个程序中,每个定义都以不同的形式出现   翻译单元,并提供满足以下要求的定义   要求。鉴于这样一个名为D的实体在多个中定义   翻译单位,然后:

     
      
  • D的每个定义应由相同的令牌序列组成;和
  •   
     

...

您的类Plugin有两个不同的定义,一个是libplugin,另一个是libspecialplugin,因此它不符合标准。

标准没有定义结果,所以任何事情都可能发生。

答案 1 :(得分:0)

我必须添加一个巨大的免责声明,"与vtables的一切都是实施定义。"

如果插件构造函数和析构函数未在标头中内联声明,这将正常工作。它必须是对libplugin.so库中的Plugin构造函数的实际函数调用。这意味着标头必须声明构造函数和析构函数,但不要定义它们以避免生成编译器的自动版本。

看起来像是:

class Plugin
{
    public:
        Plugin();
        ~Plugin();
        virtual void doStuff();
};

另外,假设在课程结束时添加了新的虚拟功能。如果它导致vtable中的任何其他函数移动,那将破坏ABI。

然后,当构建插件基类时,它将使用额外函数创建新的vtable。然后,SpecialPlugin将调整其一个虚拟功能并完成构建。

其中一些可能取决于vtbl指针的特定编译器实现,但我已经看到它完成了。