简介
boost::shared_ptr是可以共享所有权的指针。如果有多个shared_ptr共同管理同一个对象时,只有这些shared_ptr全部与该对象脱离关系之后,被管理的对象才会被释放。通过下面这个例子先了解下shared_ptr的基本用法:
1 #include <iostream> 2 #include <string> 3 #include <boost/shared_ptr.hpp> 4 5 using namespace std; 6 7 class Book 8 { 9 private: 10 string name_; 11 12 public: 13 Book(string name) : name_(name) 14 { 15 cout << "Creating book " << name_ << " ..." << endl; 16 } 17 18 ~Book() 19 { 20 cout << "Destroying book " << name_ << " ..." << endl; 21 } 22 }; 23 24 int main() 25 { 26 cout << "=====Main Begin=====" << endl; 27 { 28 boost::shared_ptr<Book> myBook(new Book("「1984」")); 29 cout << "[From myBook] The ref count of book is " << myBook.use_count() << ".\n" << endl; 30 31 boost::shared_ptr<Book> myBook1(myBook); 32 cout << "[From myBook] The ref count of book is " << myBook.use_count() << "." << endl; 33 cout << "[From myBook1] The ref count of book is " << myBook1.use_count() << ".\n" << endl; 34 35 cout << "Reset for 1th time. Begin..." << endl; 36 myBook.reset(); 37 cout << "[From myBook] The ref count of book is " << myBook.use_count() << "." << endl; 38 cout << "[From myBook1] The ref count of book is " << myBook1.use_count() << "." << endl; 39 cout << "Reset for 1th time. End ...\n" << endl; 40 41 cout << "Reset for 2th time. Begin ..." << endl; 42 myBook1.reset(); 43 cout << "Reset for 2th time. End ..." << endl; 44 } 45 cout << "===== Main End =====" << endl; 46 47 return 0; 48 }
运行结果:
运行过程分析:
shared_ptr的管理机制其实并不复杂,就是对所管理的对象进行了引用计数,当新增一个shared_ptr对该对象进行管理时,就将该对象的引用计数加一;减少一个shared_ptr对该对象进行管理时,就将该对象的引用计数减一,如果该对象的引用计数为0的时候,说明没有任何指针对其管理,才调用delete释放其所占的内存。
1) 创建Book对象,将其分配给myBook管理,此时其使用计数为1。
2) 将myBook的所有权共享给myBook1,查看通过这2个shared_ptr查看引用计数都为2。说明当所有权共享时,通过每个shared_ptr查看到的引用计数值是一样。
3) 剥夺myBook的所有权,通过myBook查看到的引用计数值变为0(脱离关系,变为0),通过myBook查看到的引用计数值变为1(共享者少了1个,减1)。
4) 当剥夺最后一个shared_ptr对其控制对象的所有权时,被管理的对象将被释放。
内部实现
下面是boost::shared_ptr内部实现所涉及到的类关系图(部分类属性和成员函数省略):
shared_ptr<T>:px代表指向具体对象的指针;pn保存引用计数相关信息。shared_ptr的核心就是shared_count,因为整个shared_ptr实现都没有出现对引用计数的具体操作,比如+1 -1等。而每一次需要用到对引用计数的操作都调用了shared_count内部封装的函数,比如:swap、==、get_deleter、use_count等。
shared_count:内部包含sp_counted_base *pi_,该成员在shared_count的构造函数里初始化。
1 // 构造函数(通过被管理对象类型构造) 2 template<class Y> explicit shared_count( Y * p ): pi_( 0 ) 3 { 4 ... 5 pi_ = new sp_counted_impl_p<Y>( p ); 6 ... 7 } 8 9 // 拷贝构造函数 10 shared_count(shared_count const & r): pi_(r.pi_) // nothrow 11 { 12 if( pi_ != 0 ) pi_->add_ref_copy(); 13 }
sp_count_base:该类主要对引用计数进行管理,used_count是被引用的此时;weak_count涉及到weak_ptr的相关内容,这里就先不讨论。add_ref_copy()内部会对used_count进行自加。release()内部调用dispose(),不过dispose()需要派生类自己实现。创建派生类对象时,会先初始化sp_count_base类对象中的use_count为1。
下面我们简化下场景:创建一个名为myBook的shared_ptr对象来管理一个Book对象,再通过myBook新建名为myBook1的shared_ptr对象。通过如下时序图来回顾下整个流程:
总结
与裸指针相比,shared_ptr 会有一点点额外的空间代价。我还没有发现由于这些代价太大而需要另外寻找一个解决方案的情形。不要去创建你自己的引用计数智能指针类。没有比使用 shared_ptr 智能指针更好的了。和前面介绍的boost::scoped_ptr相比,boost::shared_ptr可以共享对象的所有权,因此其使用范围基本上没有什么限制,自然也可以使用在stl的容器中。另外它还是线程安全的,但boost::shared_ptr并不是绝对安全,下面几条规则能使我们更加安全的使用boost::shared_ptr:
- 避免对shared_ptr所管理的对象的直接内存管理操作,以免造成该对象的重释放。
- shared_ptr并不能对循环引用的对象内存自动管理(这点是其它各种引用计数管理内存方式的通病)。
- 不要构造一个临时的shared_ptr作为函数的参数(有内存泄露风险,取决于编译器厂商的实现)。
1 void f(shared_ptr<int>, int); 2 int g(); 3 4 void ok() 5 { 6 shared_ptr<int> p(new int(2)); 7 f(p, g()); 8 } 9 10 void bad() 11 { 12 f(shared_ptr<int>(new int(2)), g()); 13 }
关于这个问题更多相关的内容:http://www.boost.org/doc/libs/1_52_0/libs/smart_ptr/shared_ptr.htm
参考
- TianFang - Boost智能指针——shared_ptr:http://www.cnblogs.com/TianFang/archive/2008/09/19/1294521.html
- qgx2009 - shared_ptr相关源代码分析:http://blog.chinaunix.net/uid-21706718-id-3563640.html
- Bj?rn Karlsson:Beyond the C++ Standard Library: An Introduction to Boost(《超越C++标准库:Boost库导论》)