假设我有一个名为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();
我猜会发生以下情况之一:
- 应用程序崩溃
- 调用
Plugin::doMoreStuff()
方法
libspecialplugin
库是否包含libplugin
可以用来确定哪些方法被重写的信息,即使在运行时也是如此?我对这里到底应该发生什么有点模糊。
在使用这两个库的任何程序中,在两个不同的翻译单元中对同一类(Plugin
)进行不同的定义,实际上违反了"一个定义规则"。
该标准规定(C++11 ISO 14882:2011,§3.2第5段):
类类型可以有多个定义(第9条)。。。在程序中,前提是每个定义都出现在不同的翻译单位,并提供满足以下要求的定义要求。给定这样一个名为D的实体在多个翻译单位,然后:
- D的每个定义应由相同的令牌序列组成;以及
。。。
您的类Plugin
有两个不同的定义,一个在libplugin中烘焙,另一个在librspecialplugin中,因此它不符合标准。
这个标准没有定义结果,所以任何事情都可能发生。
我必须添加一个巨大的免责声明,即"与vtables有关的一切都是由实现定义的。">
如果插件构造函数和析构函数没有在头中内联声明,这将很好地工作。它必须是对libplugin.so库中Plugin构造函数的实际函数调用。这意味着头必须声明构造函数和析构函数,但不能定义它们,以避免生成编译器的自动版本。
它看起来像:
class Plugin
{
public:
Plugin();
~Plugin();
virtual void doStuff();
};
还假设在类的末尾添加了新的虚拟函数。如果它导致vtable中的任何其他函数移动,则会破坏ABI。
然后,当构建插件基类时,它将使用额外的函数创建新的vtable。然后SpecialPlugin将调整其一个虚拟功能并完成构建。
其中一些可能取决于vtbl指针的特定编译器实现,但我已经看到了这种情况。