翻译来自 : http://www.boost.org/doc/libs/1_49_0/libs/smart_ptr/shared_ptr.htm
1. 简介:
Shared_ptr 模板类用来保存动态申请的对象指针,一般在C++中动态申请使用new表达式. 当最后一个指向对象的share_ptr被销毁或者重置时,对象将会被delete掉. 每一个shared_ptr都实现了拷贝构造以及赋值操作,因此可以在C++标准库的容器中使用.另外,shared_ptr也实现了比较操作符,这使得shared_ptr可以使用在关联类容器(如 map set )中.一般来说,shared_ptr不能用来存放 数组指针. shared_array提供了存放数组指针的功能.
因为shared_ptr的实现使用了引用计数,两个保存有相同对象的shared_ptr相互引用会导致循环引用.比如:
using namespace boost;
class FooB;
class FooA
{
public:
shared_ptr<FooB> b_;
};
class FooB
{
public:
shared_ptr<FooA> a_;
};
int main( void )
{
shared_ptr<FooA> fooa; // fooa 计数为 1
shared_ptr<FooB> foob; // foob 计数为 1
fooa.b_ = foob; // foob 计数为2
foob.a_ = fooa; // fooa 计数为2
return 1;
}
这样fooa 通过 foob 引用到自己保存的实例. 当 fooa 析构时,只是把引用计数降到1,并不会执行delete操作. foob也是一样.由此导致内存泄露;可以使用
weak_ptr 来"打破循环".
模板的类型参数T就是shared_ptr对象所指向的类型.shared_ptr以及大部分他的成员函数都对T没有要求, T可以是不完整类型(incomplete type),或者 void.构造函数(constructor),以及 重置(reset) 会对类型T提出要求.这些需求会在下面讲到.
只要T*可以隐式的转换成为U*,shared_ptr<T>就可以隐式的转换成为shared_ptr<U>. 实际上,shared_ptr<T>可以隐式的转换成为 shared_ptr<T const> shared_ptr<U>(如果U是一个可以访问的基类),以及 shared_ptr<void>.
2. 最佳实践
使用智能指针的一个简单的原则是:总是使用一个具名智能指针变量去保存new的返回值,这样可以有效的防止内存泄露. 每一个出现new 关键字的地方,代码都应有下面类似的格式:
shared_ptr<T> p(new Y);
当然,也可以使用其他类型的智能指针.T 与 Y有相同的类型. 也可以给Y的构造函数传参.
如果遵循这个原则,那么以后的代码中将不会有显式的delete, try/catch 结构也会减少.
避免使用未命名临时shared_ptr,比如:
void f(shared_ptr<int>, int);
int g();
void ok()
{
shared_ptr<int> p(new int(2));
f(p, g());
}
void bad()
{
f(shared_ptr<int>(new int(2)), g());
}
函数ok 严格的遵守了这个原则,然而函数bad构建了临时的,未命名的shared_ptr对象,从而有可能导致内存泄露.这主要是因为函数参数的计算顺序并不是确定的.在 函数 bad中,
计算顺序有可能是 new int(2) --> shared_ptr<int>(new int(2)) --> g() ,也有可能是 new int(2) --> g()--> shared_ptr<int>(new int(2)) ,这时如果函数g()抛出异常,这时shared_ptr还没有构造,因此无法删除 new int(2), 从而引发内存泄露. 可以通过Herb Sutter's treatment 更多的了解这个问题.
上面的这个异常安全问题也可以通过使用 make_shared 或者 allocate_shared 这两个工厂函数来消除.