我承认这个问题听起来很普遍。但毕竟,从DLL导出类是一个普遍而困难的话题,坦率地说,我目前在一个相当普遍的层面上感到困惑。
简短的问题:C ++和DLL中的面向对象编程如何组合在一起?
长问题:在阅读this和this之后,我有点失望和困惑,因为我想知道如果DLL边界,面向对象编程如何与DLL一起工作不允许共享对象(假设两个DLL使用了不同的编译器或编译器版本)。导出类的唯一选项是这些(如here或here所述):
例如,我想将常用的实用程序类放在一个DLL中,然后我在其他DLL中的几个类中使用它们,这些DLL本身也在其他DLL中使用。我怎样才能做到这一点?这是一种不明确的方式来组织我的课程吗?
加分问题:如果我导出一个具有指向实现的指针的类,这相当于导出纯虚拟类和工厂函数吗?或者导出的成员函数必须是虚拟的吗?
编辑:如果重要,我使用Visual Studio 2010在Windows 7上。迁移vom较旧的Visual Studio让我对这个问题很敏感。答案 0 :(得分:3)
TL; DR :这取决于。
当使用相同的编译器和编译器版本构建两个DLL(或DLL和可执行文件)时,它们使用相同的运行时(调试或发布),并且它们链接到运行时的动态版本, 你想做什么,就可以做什么。例如,跨DLL边界删除。
当一个DLL链接到调试运行时,另一个链接到发布运行时,事情变得更复杂。这是因为调试运行时具有不同的STL模板,用于调试目的。例如,您无法操纵从发布DLL中的调试DLL分配的std :: vector,反之亦然。只要您将这些STL模板限制在正确的DLL中,事情就应该有效。显然,如果您自己暴露不同的ABI,例如通过在#ifndef NDEBUG
块中声明成员,您将遇到问题。
根据调试DLL使用的模板,您可能需要使用_CRT*
定义强制执行此操作,具体取决于调试DLL使用的模板。
您还应该从同一个DLL创建和销毁对象。
当编译器版本不匹配时,至少有一个DLL与静态运行时链接时。
你会遇到大量的ABI问题。唯一安全的做法是通过extern "C"
(广泛地)依赖C ABI。如果在运行时加载DLL,这也可能是您想要做的。
此时要做的好事:
dllexport
)任何内容。__declspec(dllexport)
然后客户端将使用仅包头的包装器,因此每次调用DLL代码都将使用C ABI进行,这非常稳定,不会破坏。
答案 1 :(得分:1)
自从我写one of the questions you linked以来已经很长时间了,但让我试着帮忙。
简短的问题:C ++和DLL中的面向对象编程如何组合在一起?
这完全取决于您的对象所暴露的内容。如果你的对象返回普通C值,你应该没问题。如果您的对象返回包含plain-C值的POD类,那么您也应该没问题。我试图在那个问题/答案对中尝试解决的头痛几乎完全与STL有关。
为了回答自然的后续问题,STL使用DLL播放非常糟糕。由于不同的打包,成员重新排序等原因,C ++类具有固有的交叉编译器兼容性问题.STL增加了额外的潜在不兼容层,因为当内置到调试DLL中时,可以添加额外的成员。
例如,我想将常用的实用程序类放在一个DLL中,然后我在其他DLL中的几个类中使用它们,这些DLL本身也在其他DLL中使用。我怎么能这样做?
在成功传递STL类型失败后,我最终将我的C ++类包装在一个层中,这些层将它们转换为C语言对应的。在可能的情况下,我返回了基本数据类型。我必须分配内存(string
,vector
等)我最终得到了c风格的创建/删除功能。我相信我仍然暴露了一个纯虚拟接口来保护尽可能多的实现细节;我只是没有尝试直接跨越DLL边界传递任何STL对象。
如果我导出一个具有指向实现的指针的类,这相当于导出纯虚拟类和工厂函数吗?或者导出的成员函数必须是虚拟的吗?
如果我正确理解了这个问题,你想要同时做到这两点。
struct MyDLL
{
virtual void DoSomething() = 0;
virtual int AddSomething(int argument1, int argument2) = 0;
}
extern "C" __declspec(dllexport) MyDLL* GetMyDLL();
现在,您的来电者可以拨打GetMyDLL
并指向您的班级指针,您可以安全地在幕后实施虚拟功能,而无需担心来电者看到您正在做什么。