【问题标题】:How does a class vtable work across shared libraries?类 vtable 如何跨共享库工作?
【发布时间】:2017-01-11 05:46:37
【问题描述】:

假设我有一个名为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() 方法被调用

libspecialplugin 库是否包含libplugin 可以用来确定其哪些方法被覆盖的信息——即使在运行时也是如此?我对这里到底应该发生什么有点模糊。

【问题讨论】:

  • 没有“共享库”之类的东西,这是一个矛盾。您所说的是共享对象代码,它的工作方式与常规对象代码一样,只是始终与位置无关(如今,常规对象代码通常与位置无关,但并不总是如此)。

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


【解决方案1】:

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

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

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

  • D 的每个定义都应由相同的标记序列组成;和

...

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

标准未定义此结果,因此任何事情都可能发生。

【讨论】:

    【解决方案2】:

    我必须添加一个巨大的免责声明,即“与 vtables 相关的一切都是实现定义的。”

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

    看起来像:

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

    还假设在类的末尾添加了新的虚函数。如果它导致 vtable 中的任何其他函数移动,那将破坏 ABI。

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-07-29
      • 2020-05-03
      • 1970-01-01
      • 2013-02-09
      • 2014-03-11
      • 1970-01-01
      • 2021-06-22
      • 1970-01-01
      相关资源
      最近更新 更多