在模块(exes和dll)之间使用STL(TR1)shared_ptr是否安全?

时间:2008-12-05 20:21:04

标签: c++ visual-c++ memory-management stl shared-ptr

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

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

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

我试图避免在分配模块中对免费对象进行特殊调用。 (或类似于“删除此”本身)。如果这一切看起来有点hacky,我正在使用它进行单元测试。如果您曾尝试对现有C ++代码进行单元测试,则可以了解有时需要 创意 的方式。我的内存由EXE分配,但最终将在DLL中释放(如果引用计数按我认为的方式工作)。

5 个答案:

答案 0 :(得分:14)

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

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

如果您尝试编写基于DLL的松散耦合插件,这可能会让您感到困惑。这也是COM允许DLL决定何时可以卸载的原因,而不是让COM服务器要求卸载它们。

答案 1 :(得分:12)

你开始看到令人难以置信的惊人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>() );

(即,如果没有删除者,就没有shared_ptr这样的东西。)

由于在删除器上执行了类型擦除,所调用的delete 总是是来自实例化 {{1}的DLL的那个永远不会是最后一个shared_ptr超出范围的DLL中的那个(即调用删除器的shared_ptr将通过指向原始{{1}放置的函数的指针来调用它。 })。

将此与shared_ptr进行比较,shared_ptrauto_ptr运算符直接嵌入其(内联)析构函数中,这意味着销毁的DLL的delete使用delete,产生与删除裸指针相同的问题。

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

答案 2 :(得分:7)

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

Boost的文档声称它与TR1 100%兼容,所以希望没有什么误导:

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

答案 3 :(得分:2)

我认为在模块中使用std中的任何类都是安全的。

即:如果模块使用完全相同的运行时库,并且完全相同的编译器开关和选项,则应该是安全的。

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

答案 4 :(得分:2)

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

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