16

我知道在一个模块中添加新内容并在另一个模块中删除它通常会导致 VC++ 出现问题。不同运行时的问题。如果我没记错的话,将模块与静态链接的运行时和/或动态链接的版本控制不匹配混合起来都会搞砸。

但是,跨模块使用 VC++ 2008 的 std::tr1::shared_ptr 是否安全?

由于只有一个版本的运行时甚至知道什么是 shared_ptr,所以静态链接是我唯一的危险(现在......)。我以为我已经读过 boost 的 shared_ptr 版本可以像这样安全使用,但我使用的是 Redmond 的版本......

我试图避免在分配模块中对释放对象进行特殊调用。(或类本身中的“删除这个”之类的东西)。如果这一切看起来有点 hacky,我正在使用它进行单元测试。如果您曾经尝试过对现有 C++ 代码进行单元测试,那么您就会明白有时您需要发挥多大的创造力。我的内存由 EXE 分配,但最终将在 DLL 中释放(如果引用计数按我认为的方式工作)。

4

5 回答 5

16

释放内存是安全的,只要它都来自相同的内存管理上下文。您已经确定了最常见的问题(不同的 C++ 运行时);拥有单独的堆是您可能遇到的另一个不太常见的问题。

另一个您没有提到但可能会因共享指针而加剧的问题是,当一个对象的代码存在于 DLL 中并由 DLL 创建时,但 DLL 之外的另一个对象最终会引用它(通过共享指针)。如果该对象在 DLL 被卸载后被销毁(例如,如果它是模块级静态的,或者如果 DLL 被显式卸载FreeLibrary(),则共享对象的析构函数将崩溃。

如果您尝试编写基于 DLL 的松散耦合插件,这可能会给您带来麻烦。这也是 COM 让 DLL 决定何时可以卸载它们的原因,而不是让 COM 服务器按需卸载它们。

于 2008-12-05T23:10:09.993 回答
14

你开始看到是多么令人难以置信shared_ptr:)

跨 DLL 边界的安全正是shared_ptr设计的初衷(当然,除其他外)。

与其他人所说的相反,您甚至不需要在构造时传递自定义删除器shared_ptr,因为默认值已经类似于

template <typename T>
struct default_deleter {
    void operator()( T * t ) { delete t; }
};

shared_ptr<Foo> foo( new Bar );

相当于

shared_ptr<Foo> foo( new Bar, default_deleter<Bar>() );

(即,没有删除器就没有 a 这样的东西shared_ptr)。

由于在删除器上执行的类型擦除,delete被调用的将始终是 DLL 中实例化的那个shared_ptr,而不是来自 DLL 中最后一个shared_ptr超出范围的那个(即,shared_ptr调用删除器将通过一个指向原始函数放在那里的函数的指针shared_ptr)。

将此与 比较auto_ptr,后者将delete运算符直接嵌入其(内联)析构函数中,这意味着使用破坏DLL的deleteDLL ,产生与删除裸指针相同的问题。auto_ptr

通过同样的技术,总是保存在shared_ptrs 中的多态类甚至不需要虚拟析构函数,因为删除器总是会调用正确的析构函数,即使最后一个shared_ptr超出范围的是为基类实例化的析构函数。

于 2011-04-29T17:02:41.530 回答
7

如果您担心,请使用带有删除器参数的 shared_ptr 构造函数的形式。删除器可以回调分配对象的模块,以便删除发生在正确的上下文中。

Boost 的文档声称它与 TR1 100% 兼容,所以希望这不会引起任何误导:

http://www.boost.org/doc/libs/1_37_0/libs/smart_ptr/shared_ptr.htm#constructors

于 2008-12-05T23:09:22.477 回答
2

我猜想它与std跨模块使用任何类一样安全。

也就是说:如果模块使用完全相同的运行时库,以及完全相同的编译器开关和选项,则应该是安全的。

永远不要使用静态运行时库,因为每个模块都会在其中获得所有全局变量的自己的实例。

于 2008-12-05T20:47:56.943 回答
2

我在一般主题上看到的最佳建议是,应该在分配内存的同一上下文中释放内存。但是,这并不排除库传回应用程序代码应该释放的指针,所以我会说您以这种方式传递 shared_ptr 可能是安全的,因为这是相同的一般情况。

如果您的系统语义意味着指针实际上是从您的 exe 传输到您的 dll(在所有权意义上),那么 auto_ptr 可能是一个更好的解决方案。但是,如果您的指针是真正共享的,那么 shared_ptr 可能是最好的解决方案。

于 2008-12-05T20:56:16.067 回答