因此,我正在阅读COM和DLL,似乎,如果我要更新DLL的实现(如果所需的内存超过了旧的需求),则需要使用DLL重新编译程序。与更新的DLL。所以我只是想知道,什么时候使用DLL代替COM是正确的?重新编译所有程序似乎几乎是不可能的,因为它们已经更新了依赖性。

此外,还有一个简短的问题:为什么.NET中二进制封装不成问题?我怎么能更改一个.NET中找到的DLL,而无需重新编译我的旧项目,又不用使用pimpl / opaque指针和诸如此类的东西呢?

最佳答案

二进制兼容性要求调用者和被调用者就通用ABI(应用程序二进制接口)达成协议。

在Windows和Linux上,操作系统都指定了用于C调用的标准ABI,它允许从任何其他语言到C库的调用。只要C ++仅在DLL内部使用并且外表面使用extern "C"和C兼容类型(例如指针和基元以及C兼容类型的原始数据组合),这也使C ++对DLL开发有用。

在Linux和Windows上,C ++ ABI与C ++运行时库关联。在Linux上,人们习惯于自欺欺人地认为C ++共享对象也具有操作系统提供的ABI,因为只有一个广泛使用的C ++库-g ++的libstdc++,并且该库花了很长时间来保存相同的ABI。因此,C ++ 11 libstdc++之前的ABI是事实上的标准,没有人遇到混合ABI的麻烦。现在C ++ 11强制libstdc++破坏二进制兼容性,而clang的libc++越来越流行,因此Linux开发人员面临Windows开发人员长期以来所熟悉的问题。

在Windows上,从来没有C ++标准库以完全相同的方式占据主导地位,部分原因是每个版本的OS供应商工具(Microsoft Visual C ++)都引入了不兼容性。是的,有些事情基本上保持不变(名称修改),但其他事情发生了根本性的变化(分配器获得了低碎片堆支持,容器得到了调试迭代器,等等)。

具有讽刺意味的是,Windows上的ABI标准化现在比Linux1上更好,因为Windows ABI指定了一种C ++功能-vtable布局-支持COM。

本机C ++的底线是您可以在DLL中自由使用它,但是为了进行解耦,DLL的公共表面只需要使用OS ABI中指定的功能-extern "C"名称,C兼容类型和(仅Windows)抽象基类仅由虚拟函数组成(没有静态成员,没有非静态数据,所有非静态函数都是虚拟的,并且构造函数受到保护)。

但是,如果您可以进行耦合,则可以获得更多功能。如果应用程序中的每个组件都同意.NET ABI,则其所有功能都将可用。 .NET的ABI的开发考虑了向后和向前的兼容性,它从C ++的不兼容性以及以前的尝试(例如Java)中吸取了教训。

由于.NET ABI非常稳定,因此与之耦合是一个可行的决定,就像许多与C ++ 11 stdlibc++之前的ABI耦合的Linux开发人员一样。但是,如果您破坏了兼容性,则可能会丢失所有可重用的库,这就是为什么将耦合到任何特定的Windows C ++库视为一个坏主意的原因-升级编译器要求您拥有每个库的新版本。
当即使只有一个组件卡在旧版本上也无法采用新的编译器时,您将失去对其他已切换组件的新开发的机会。这就是为什么Windows上的C ++ ABI耦合被广泛认为是一个非常糟糕的主意的原因。以及为什么库作者很少以二进制形式分发Linux代码-他们将其留给每个发行版来选择ABI,使用它进行一致地编译,并提供与其兼容的二进制软件包。

当然,这只能解决源代码外部的更改(对库类型的布局更改,参数传递顺序等)。如果您的源代码发生更改(例如,通过添加新的函数参数或更改类类型中的数据成员),则无论ABI有多好(明确规定/稳定),您都将不兼容。 .NET通过两阶段编译系统避免了其中一些影响-依赖项更改时,确实会重新编译整个世界,因为JIT编译是在运行时完成的。但是即使在.NET中,某些更改(例如添加新的函数重载)也需要重新编译源代码才能生效。



实际上我不确定,是否也可以在Linux的操作系统级别上指定vtable-layout。重要的是Linux C ++二进制兼容性不再是通用的,这使许多实际上依赖于事实上的标准编译器提供的ABI的开发人员感到惊讶,而他们却假设OS ABI永远保证了通用兼容性。因此,Windows开发人员已经学会了管理二进制兼容性的不足,处于更好的位置。

关于c# - 如果C++中的DLL不支持二进制封装,那么什么时候才可以使用它们呢?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/29050931/

10-12 21:25