我看到一些文章说在dll之间传递STL和shared_ptr是不安全的,因为
- ,这可能导致内存损坏和
- 它们可能具有不同的C++运行时实现,因此会导致未定义的行为
问题1:如果我用相同的编译器版本和标志编译两个库,这是真的吗?考虑到C++运行时是相同的,并且dll通常是从调用进程的虚拟地址空间分配的内存,这两点仍然有效。
问题2:我看到的解决方案引用了在堆上的一个dll中创建对象,并传递一个自定义析构函数,该析构函数在smart_ptr创建时对其进行清理,然后将其传递出去,以便在引用丢失时可以安全地从创建它的第二个第一个dll中删除它。什么时候需要?这不是ABI稳定的,是吗?这不是只需要dll在它自己的地址空间中创建对象,并且你不希望从中删除另一个dll吗?这不是默认的,是吗?
问题3:这是否同样与静态库有关,比如说使用不同的编译器版本构建的静态库。
这里有一个在dll之间使用std库类型的具体实际问题。
相同的std库,两个dll。
Dll1定义了一个类Foo。
Dll2将Foo封装在一个共享ptr中。Foo的销毁代码是用Dll2中的cose类型擦除的。
Dll2已卸载。
最后对共享ptr的引用消失了。
程序崩溃。
现在,我已经解决了这个问题;你需要真正关心你在哪里创建了共享ptr。如果您的代码能够广泛地做到这一点(例如,写时复制指针(,那么无论何时创建新的共享ptr,都必须调用Dll1以获得新的共享pt。
话虽如此,我们仍然这样做。我只是说这不是免费的。即使我自己重新实现了共享ptr,也会出现同样的问题;这是一个代码问题,它可以动态生成代码(即带有rype擦除的模板(,并将其附加到multi.dll环境中的值。
(1-2(DLL有单独的堆。因此,在一个dll中进行分配而在另一个中进行解除分配会导致崩溃。这不仅仅是shared_ptr
或STL的问题,而是一个非常普遍的问题,它带来了很多困难。shared_ptr
可以用来解决这个问题:在shared_ptr
中,您可以在正确的dll中指定调用delete的deleter函数。
(3( 当将库链接在一起时,不同的编译器版本可能会导致严重的问题,尤其是作为静态库。想象一下STL中的一些实现差异。它很容易导致崩溃或代码损坏。例如,在MSVC中,将调试与发布链接可能会导致std::string
崩溃。MSVC所做的一个警告是在使用原子时——他们说在VS2015中,某些类型的atmics存在漏洞,所以不要与该版本的VS中的库链接。
为了安全地链接不同的编译器/版本,dll是必要的。通常,人们试图在DLL中实现一个非常基本的接口,这样它将尽可能安全。