【问题标题】:Is it safe to pass shared_ptr and other STLs between dlls and so's if built using the same compiler and compatible compiler flags?如果使用相同的编译器和兼容的编译器标志构建,在 dll 之间传递 shared_ptr 和其他 STL 是否安全?
【发布时间】:2020-10-22 02:36:24
【问题描述】:

我看到文章指出在 dll 之间传递 STL 和 shared_ptr 是不安全的,因为

  1. 这可能会导致内存损坏和
  2. 它们可能具有不同的 C++ 运行时实现,因此会导致未定义的行为。

问题 1:如果我使用相同的编译器版本和标志编译两个库,这是真的吗?考虑到 C++ 运行时相同,并且 dll 通常从调用进程的虚拟地址空间分配内存,这两点仍然有效。

问题 2:我看到解决方案涉及在堆上的一个 dll 中创建对象并传递一个自定义析构函数,该析构函数在创建 smart_ptr 时对其进行清理并将其传递,以便可以从创建它的第二个第一个 dll 中安全地删除它当参考丢失时。什么时候有这个需求?这不是 ABI 稳定的吗?这不是只需要 dll 在它自己的地址空间中创建对象并且您不希望另一个 dll 从中删除吗?这不是默认的吧?

问题 3:这是否同样与静态库相关,彼此交谈说使用不同的编译器版本构建。

【问题讨论】:

    标签: c++ dll shared-libraries shared-ptr abi


    【解决方案1】:

    这是一个在 dll 之间使用标准库类型的具体实际问题。

    相同的标准库,两个 dll。

    Dll1 定义了一个类 Foo。

    Dll2 将 Foo 包装在一个共享 ptr 中。 Foo 的销毁代码在 Dll2 中用 cose 类型擦除。

    Dll2 已卸载。

    对共享 ptr 的最后引用消失。

    程序崩溃。

    现在,我已经解决了这个问题;你需要非常小心你在哪里创建了共享 ptrs。如果您有广泛执行此操作的代码(例如,写入指针的副本),则无论何时创建新的,都必须调用 Dll1 以获取新的共享 ptr。

    话虽如此,我们仍然这样做。我只是说它不是免费的。即使我自己重新实现了共享 ptr,也会出现同样的问题;这是动态生成代码(即带有 rype 擦除的模板)并将其附加到多 dll 环境中的值的代码的问题。

    【讨论】:

      【解决方案2】:

      (1-2) DLL 具有单独的堆。因此,在一个 dll 中分配并在另一个中释放会导致崩溃。这不仅仅是shared_ptr 或仅STL 的问题,而是一个非常普遍的问题,它带来了很多困难。 shared_ptr 可用于解决此问题:在shared_ptr 中,您可以指定在正确的dll 中调用delete 的deleter 函数。

      (3) 当您将库链接在一起时,尤其是作为静态库,不同的编译器版本可能会导致严重的问题。想象一下 STL 中的一些实现差异。它很容易导致崩溃或代码损坏。例如,在 MSVC 中,将调试与发布链接可能会导致 std::string 崩溃。 MSVC 发出的警告之一是在使用原子时 - 他们说在 VS2015 中,某些类型的原子存在错误,因此不要链接该版本 VS 中的库。

      为了安全地链接不同的编译器/版本,dll 是必要的。通常,我们会尝试在 DLL 中实现一个非常基本的接口,以尽可能保证 ABI 安全。

      【讨论】:

      • 我不同意其中一些说法,但确实看到了其余部分的价值。 Std::shared_ptr 的删除是类型擦除的,因此默认实现与传递自定义删除器相同。不安全的是没有类型擦除的 unique_ptr。 “要安全地链接不同的编译器/版本,dll 是必要的。通常,人们会尝试在 DLL 中实现一个非常基本的接口,因此它会尽可能 ABI 安全。”它是一个 DLL 的事实增加了 0 的好处。要链接不同的编译器版本,您需要一个稳定的 ABI,我认为静态库也是如此。
      • @rstr1112 “它是一个 DLL 的事实增加了 0 个好处......”你绝对错了。与静态库不同,除非明确声明,否则 DLL 不会公开任何链接符号。因此,可以在两个不同的 dll 中使用完全不同的 STL 版本。在静态库中使用它会导致即时代码损坏,因为所有符号都被暴露并且重复项将被随机删除。
      • @rstr1112 使用非常不同的编译器甚至语言,通常会创建 C 风格的接口供外部使用。在 dll 中公开特定的库类可能会导致问题,因为用户可能正在导入相同的库但不同的版本会导致冲突。因此,在编译器/代码库之间共享多少 ABI/库与接口的高级程度之间存在一定的权衡。
      • 基本上您是在说,您可以通过稳定的 C ABI 在动态库之间进行交互并避免损坏,但如果您使用不属于 C ABI 的组件,则不能。静态库也是如此,您可以通过稳定的 C ABI 在静态库之间进行交互并避免损坏,但如果您使用不属于 C ABI 的组件,则不能。因此 dll = 0 对 ABI 稳定性有好处。
      • @rstr1112 我相信您对删除shared_ptr 是正确的,但仍然存在用户可能拥有与dll 不同版本的shared_ptr 的问题。虽然如果编译器/STL一致应该没问题。
      猜你喜欢
      • 1970-01-01
      • 2012-10-03
      • 2020-09-23
      • 1970-01-01
      • 1970-01-01
      • 2012-03-13
      • 2021-12-10
      • 2018-07-01
      • 1970-01-01
      相关资源
      最近更新 更多