我可以在DLL中组织类吗

Can I organize classes in DLLs?

本文关键字:DLL 我可以      更新时间:2023-10-16

我承认这个问题听起来很笼统。但毕竟,从DLL导出类是一个普遍而困难的话题,坦率地说,我目前在相当普遍的层面上感到困惑。

简短的问题:C++中的面向对象编程和DLL是如何结合在一起的?

长问题:读过这篇文章后,我有点失望和困惑,因为我想知道如果DLL边界不允许共享对象(假设两个DLL使用了不同的编译器或编译器版本),面向对象编程如何与DLL一起工作。导出类的唯一选项是(如这里或这里所解释的):

  • 导出create和delete方法(C风格,悬挂指针的危险,没有对象作为参数,丑陋)
  • 导出一个纯虚拟类和一个工厂函数,该函数创建从纯虚拟类派生的实际实现类的实例(需要继承,需要处理对象删除)

例如,我想把公共实用程序类放在一个DLL中,然后在其他DLL的几个类中使用,这些类本身也在其他DLL中使用。我该怎么做?这是不是一种定义不清的上课方式?

附加问题:如果我导出一个具有指向实现的指针的类,这相当于导出一个纯虚拟类和一个工厂函数吗?还是导出的成员函数必须是虚拟的?

编辑:如果重要的话,我在使用Visual Studio 2010的Windows7上。迁移到较旧的Visual Studio使我对这个问题很敏感。

TL;DR:这取决于情况。

简单案例

当两个DLL(或一个DLL和一个可执行文件)是用相同的编译器和编译器版本构建的,并且它们使用相同的运行时风格(调试或发布),并且它们链接到运行时的动态版本时,您可以随心所欲。例如,跨DLL边界删除。

不那么简单的案例

当一个DLL链接到调试运行时,另一个链接到发布运行时时,事情会变得更加复杂。这是因为调试运行时具有不同的STL模板,用于调试目的。例如,您不能操作从发布DLL中的调试DLL分配的std::向量,反之亦然。只要您将这些STL模板限制在正确的DLL中,一切都应该正常。显然,如果您自己公开不同的ABI,例如在#ifndef NDEBUG块中声明一个成员,就会遇到问题。

根据调试DLL使用的模板,您可能需要使用_CRT*定义来强制执行一些操作。

您还应该从同一个DLL创建和销毁对象。

可怕的(又称现实世界)案例

当编译器版本不匹配时,当至少有一个DLL与静态运行时链接时打开。

你会遇到很多ABI问题。唯一安全的做法是通过extern "C"(广泛地)依赖C ABI。如果在运行时加载DLL,这可能也是您想要做的。

在这一点上做一件好事:

  • 用C编写DLL++
  • 不要使用C++ABI公开(dllexport)任何内容
  • 围绕C++代码编写一个薄薄的C包装器
  • 使用__declspec(dllexport)公开此C API
  • 围绕公开的C API编写一个仅限头的C++包装器

然后,客户端将使用您的仅头的包装器,因此对DLL代码的每次调用都将使用C ABI进行,它非常稳定,不太可能中断。

我已经很久没有写你链接的问题了,但让我试着帮你。

简短的问题是:C++中的面向对象编程和DLL是如何结合在一起的?

这完全取决于你的物体暴露了什么。如果您的对象返回的是纯C值,那么您应该没事。如果您的对象返回包含纯C值的POD类,那么您也应该可以。在那对问答中,我试图解决的头痛问题几乎完全与STL有关。

为了回答自然的后续问题,STL在DLL中的表现非常糟糕。由于不同的封装、成员重新排序等原因,C++类存在固有的跨编译器兼容性问题。STL增加了一层潜在的不兼容性,因为当构建到调试DLL中时,可以添加额外的成员。

例如,我想把公共实用程序类放在一个DLL中,然后在其他DLL的几个类中使用,这些类本身也在其他DLL中使用。我该怎么做?

在未能成功传递STL类型之后,我最终将C++类封装在一个层中,该层将它们转换为C类和从C类转换过来。在可能的情况下,我返回了基本的数据类型。在必须分配内存的地方(stringvector等),我最终得到了c样式的创建/删除函数。我相信我仍然公开了一个纯虚拟接口,以保护尽可能多的实现细节;我只是没有尝试直接通过DLL边界传递任何STL对象。

如果我导出一个具有指向实现的指针的类,这等效于导出一个纯虚拟类和一个工厂函数吗?还是导出的成员函数必须是虚拟的?

如果我正确理解这个问题,你两个都想做。

struct MyDLL
{
  virtual void DoSomething() = 0;
  virtual int AddSomething(int argument1, int argument2) = 0;
}
extern "C" __declspec(dllexport) MyDLL* GetMyDLL();

现在,您的调用者可以调用GetMyDLL,并有一个指向类的指针,这样您就可以安全地在后台实现虚拟函数,而不用担心调用者看到您在做什么。