c++ 插件:可以传递多态对象吗?

c++ plugin : Is it ok to pass polymorphic objects?

使用动态库时,我知道我们应该只跨边界传递普通旧数据结构。那么我们可以传递一个指向 base 的指针吗?

我的想法是应用程序和库都可以知道一个公共接口(纯虚方法,= 0)。 该库可以实例化该接口的子类型, 应用程序可以使用它。

例如,下面的代码片段安全吗?

// file interface.h

class IPrinter{
    virtual void print(std::string str) = 0;
 };

-

// file main.cpp

int main(){
    //load plugin...
    IPrinter* printer = plugin_get_printer();
    printer->print( std::string{"hello"} );
}

-

// file plugin.cpp (compiled by another compiler)

IPrinter* plugin_get_printer(){
    return new PrinterImpl{};
}

你的 class 中存在虚函数意味着你的 class 将有一个 vtable,不同的编译器以不同的方式实现 vtable。

因此,如果您在 DLL 调用中使用带有虚拟方法的 classes,而另一端使用的编译器与您正在使用的编译器不同,结果很可能是严重的崩溃。

在你的例子中,由 DLL 创建的 PrinterImpl 将有一个以特定方式构造的 vtable,但你的 main() 中的 printer->print() 调用将尝试解释 vtable IPrinter 以不同的方式解决 print() 方法调用。

此代码段不安全:

  • 您的 DLL 边界的两侧不使用相同的编译器。这意味着 name mangling (for function names) and the vtable 布局(对于虚函数)可能不相同(特定于实现。

  • 两侧的堆也可能以不同的方式管理,因此如果不在 DLL 中删除对象,则存在与删除对象相关的风险。

这个 article 很好地展示了二进制兼容接口的主要挑战。

然而,您可以将指针作为 POD 的一部分传递到镜像的另一侧,只要另一部分不自行使用它即可(f.ex:您的应用将指针传递给DLL 的配置对象。稍后另一个 DLL 函数 returns 指向您的应用程序。然后您的应用程序可以按预期使用它(至少如果它不是指向不再存在的本地对象的指针)。