一、boost 智能指针
智能指针是利用RAII(Resource Acquisition Is Initialization:资源获取即初始化)来管理资源。关于RAII的讨论可以参考前面的文章。在使用boost库之前应该先下载后放在某个路径,并在VS 包含目录中添加。下面是boost 库里面的智能指针:
(一)、scoped_ptr<T>
先来看例程:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
#include <boost/scoped_ptr.hpp>
#include <iostream> using namespace std; class X { public: X() { cout << "X ..." << endl; } ~X() { cout << "~X ..." << endl; } }; int main(void) { cout << "Entering main ..." << endl; { boost::scoped_ptr<X> pp(new X); //boost::scoped_ptr<X> p2(pp); //Error:所有权不能转移 } cout << "Exiting main ..." << endl; return 0; } |
来稍微看一下scoped_ptr 的简单定义:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
namespace boost { template<typename T> class scoped_ptr : noncopyable { private: T *px; scoped_ptr(scoped_ptr const &); scoped_ptr &operator=(scoped_ptr const &); typedef scoped_ptr<T> this_type; void operator==( scoped_ptr const & ) const; void operator!=( scoped_ptr const & ) const; public: explicit scoped_ptr(T *p = 0); ~scoped_ptr(); explicit scoped_ptr( std::auto_ptr<T> p ): px( p.release() ); void reset(T *p = 0); T &operator*() const; T *operator->() const; T *get() const; void swap(scoped_ptr &b); }; template<typename T> void swap(scoped_ptr<T> &a, scoped_ptr<T> &b); } |
与auto_ptr类似,内部也有一个T* px; 成员 ,智能指针对象pp 生存期到了,调用析构函数,在析构函数内会delete px; 如下面所说:
scoped_ptr mimics a built-in pointer except that it guarantees deletion of the object pointed to, either on destruction of the scoped_ptr or via an
explicit reset(). scoped_ptr is a simple solution for simple needs; use shared_ptr or std::auto_ptr if your needs are more complex.
从上面的话可以得知当调用reset() 函数时也能够释放堆对象,如何实现的呢?
1
2 3 4 5 6 7 8 9 10 11 |
void reset(T *p = 0) // never throws { BOOST_ASSERT( p == 0 || p != px ); // catch self-reset errors this_type(p).swap(*this); } void swap(scoped_ptr &b) // never throws { T *tmp = b.px; b.px = px; px = tmp; } |
typedef scoped_ptr<T> this_type; 当调用pp.reset(),reset 函数构造一个临时对象,它的成员px=0, 在swap 函数中调换 pp.px 与
(this_type)(p).px, 即现在pp.px = 0; //解绑
临时对象接管了裸指针(即所有权可以交换),reset 函数返回,栈上的临时对象析构,调用析构函数,进而delete px;
另外拷贝构造函数和operator= 都声明为私有,故所有权不能转移,且因为容器的push_back 函数需要调用拷贝构造函数,故也不能
将scoped_ptr 放进vector,这点与auto_ptr 相同(不能共享所有权)。此外,还可以使用 auto_ptr 对象 构造一个scoped_ptr 对象:
scoped_ptr( std::auto_ptr<T> p ): px( p.release() );
由于scoped_ptr是通过delete来删除所管理对象的,而数组对象必须通过deletep[]来删除,因此boost::scoped_ptr是不能管理数组对象的,如果
要管理数组对象需要使用boost::scoped_array类。
boost::scoped_ptr和std::auto_ptr的功能和操作都非常类似,如何在他们之间选取取决于是否需要转移所管理的对象的所有权(如是否需要作为
函数的返回值)。如果没有这个需要的话,大可以使用boost::scoped_ptr,让编译器来进行更严格的检查,来发现一些不正确的赋值操作。
(二)、shared_ptr<T>
An enhanced relative of scoped_ptr with reference counted copy semantics. The object pointed to is deleted when the last
shared_ptr pointing to it is destroyed or reset.
先来看例程:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
#include <boost/shared_ptr.hpp> #include <iostream> using namespace std; class X { public: X() { cout << "X ..." << endl; } ~X() { cout << "~X ..." << endl; } }; int main(void) { cout << "Entering main ..." << endl; boost::shared_ptr<X> p1(new X); cout << p1.use_count() << endl; boost::shared_ptr<X> p2 = p1; //boost::shared_ptr<X> p3; //p3 = p1; cout << p2.use_count() << endl; p1.reset(); cout << p2.use_count() << endl; p2.reset(); cout << "Exiting main ..." << endl; return 0; } |
图示上述程序的过程也就是:
再深入一点,看源码,shared_ptr 的实现 比 scoped_ptr 要复杂许多,涉及到多个类,下面就不贴完整源码,看下面的类图:
执行 new X); 这一行之后:
而执行 p1.use_count(); 先是 pn.use_count(); 接着 pi_ != 0? pi_->use_count(): 0; return use_count_; 即返回1.
接着执行 boost::shared_ptr<X> p2 = p1;
在shared_count 类的拷贝构造函数设置断点,然后就可以跟踪进去,如下的代码:
1
2 3 4 5 6 7 8 |
// shared_count
shared_count(shared_count
const &r): pi_(r.pi_) // nothrow
{ if( pi_ != 0 ) pi_->add_ref_copy(); }
// sp_counted_base
void add_ref_copy() { BOOST_INTERLOCKED_INCREMENT( &use_count_ ); } |
sp_counted_impl_p 对象,所以此时无论打印p2.use_count(); 还是 p1.use_count(); 都是2。
接着执行p1.reset();
1
2 3 4 5 6 7 8 9 |
// shared_ptr
void reset() // never throws in 1.30+ { this_type().swap(*this); } void swap(shared_ptr<T> &other) // never throws { std::swap(px, other.px); pn.swap(other.pn); } |
this_type() 构造一个临时对象,px = 0, pn.pi_ = 0; 然后swap交换p1 与 临时对象的成员,即现在p1.px = 0; p1.pn.p1_ = 0; 如上图。
reset 函数返回,临时对象需要析构,但跟踪时却发现直接返回了,原因跟上面的一样,因为shared_ptr 没有实现析构函数,调用的是默认的析构函
数,与上面拷贝函数同样的道理,可以在shared_count 类析构函数设置断点,因为pn 是对象成员,故析构函数也会被调用。如下代码:
1
2 3 4 5 6 7 8 9 10 11 12 13 |
//shared_count
~shared_count()
// nothrow { if( pi_ != 0 ) pi_->release(); } // sp_counted_base void release() // nothrow { if( BOOST_INTERLOCKED_DECREMENT( &use_count_ ) == 0 ) { dispose(); weak_release(); } } |
现在use_count_ 减为1,但还不为0,故 weak_release(); 两个函数没有被调用。当然此时打印 p2.use_count() 就为1 了。
最后 p2.reset(); 跟p1.reset(); 同样的流程,只不过现在执行到release 时,use_count_ 减1 为0;需要继续执行dispose(); 和
weak_release(); 如下代码:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
//sp_counted_impl_p virtual void dispose() // nothrow { boost::checked_delete( px_ ); } //sp_counted_base void weak_release() // nothrow { if( BOOST_INTERLOCKED_DECREMENT( &weak_count_ ) == 0 ) { destroy(); } } virtual void destroy() // nothrow { delete this; } |
在check_delete 中会 delete px_; 也就是析构 X。接着因为weak_count_ 减1 为0, 故执行destroy(); 函数里面delete this; 即析构自身
(sp_counted_impl_p 对象是在堆上分配的)。
说到这里,我们也可以明白,即使最后没有调用p2.reset(); 当p2 栈上对象生存期到, 需要调用shared_ptr 类析构函数,进而调用shared_count 类析
构函数,所以执行的结果也是跟reset() 一样的,只不过少了临时对象this_type()的构造。
总结一下:
和前面介绍的boost::scoped_ptr相比,boost::shared_ptr可以共享对象的所有权,因此其使用范围基本上没有什么限制(还是有一些需要遵循的
使用规则,下文中介绍),自然也可以使用在stl的容器中。另外它还是线程安全的,这点在多线程程序中也非常重要。
boost::shared_ptr并不是绝对安全,下面几条规则能使我们更加安全的使用boost::shared_ptr:
-
避免对shared_ptr所管理的对象的直接内存管理操作,以免造成该对象的重释放
-
shared_ptr并不能对循环引用的对象内存自动管理(这点是其它各种引用计数管理内存方式的通病)。
- 不要构造一个临时的shared_ptr作为函数的参数。
1
2 3 4 5 6 7 8 9 10 11 12 13 |
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()); } |
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
#include <boost/shared_ptr.hpp> #include <iostream> using namespace std; class Parent; class Child; typedef boost::shared_ptr<Parent> parent_ptr; typedef boost::shared_ptr<Child> child_ptr; class Child { public: Child() { cout << "Child ..." << endl; } ~Child() { cout << "~Child ..." << endl; } parent_ptr parent_; }; class Parent { public: Parent() { cout << "Parent ..." << endl; } ~Parent() { cout << "~Parent ..." << endl; } child_ptr child_; }; int main(void) { parent_ptr parent(new Parent); child_ptr child(new Child); parent->child_ = child; child->parent_ = parent; return 0; } |
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
namespace boost { template<typename T> class weak_ptr { public: template <typename Y> weak_ptr(const shared_ptr<Y> &r); weak_ptr(const weak_ptr &r); template<class Y> weak_ptr &operator=( weak_ptr<Y> && r ); template<class Y> weak_ptr &operator=(shared_ptr<Y> const &r); ~weak_ptr(); bool expired() const; shared_ptr<T> lock() const; }; } |
&&
is new in C++11, and it signifies that the function accepts an RValue-Reference -- that is, a reference to an argument that is about
to be destroyed.
两个常用的功能函数:expired()用于检测所管理的对象是否已经释放;lock()用于获取所管理的对象的强引用智能指针。
强引用与弱引用:
强引用,只要有一个引用存在,对象就不能释放
弱引用,并不增加对象的引用计数(实际上是不增加use_count_, 会增加weak_count_);但它能知道对象是否存在
通过weak_ptr访问对象的成员的时候,要提升为shared_ptr
如果存在,提升为shared_ptr(强引用)成功
如果不存在,提升失败
对于上述的例子,只需要将Parent 类里面的成员定义改为如下,即可解决循环引用问题:
1
2 3 4 5 |
class Parent { public: boost::weak_ptr<parent> child_; }; |
因为此例子涉及到循环引用,而且是类成员引用着另一个类,涉及到两种智能指针,跟踪起来难度很大,我也没什么心情像分析
shared_ptr 一样画多个图来解释流程,这个例子需要解释的代码远远比shared_ptr 多,这里只是解释怎样使用,有兴趣的朋友自
己去分析一下。
下面再举个例子说明lock() 和 expired() 函数的用法:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
#include <boost/shared_ptr.hpp> #include <boost/weak_ptr.hpp> #include <boost/scoped_array.hpp> #include <boost/scoped_ptr.hpp> #include <iostream> using namespace std; class X { public: X() { cout << "X ..." << endl; } ~X() { cout << "~X ..." << endl; } void Fun() { cout << "Fun ..." << endl; } }; int main(void) { boost::weak_ptr<X> p; boost::shared_ptr<X> p3; { boost::shared_ptr<X> p2(new X); cout << p2.use_count() << endl; p = p2; cout << p2.use_count() << endl; /*boost::shared_ptr<X> */ p3 = p.lock(); cout << p3.use_count() << endl; if (!p3) cout << "object is destroyed" << endl; else p3->Fun(); } /*boost::shared_ptr<X> p4 = p.lock(); if (!p4) cout<<"object is destroyed"<<endl; else p4->Fun();*/ if (p.expired()) cout << "object is destroyed" << endl; else cout << "object is alived" << endl; return 0; } |
从输出可以看出,当p = p2; 时并未增加use_count_,所以p2.use_count() 还是返回1,而从p 提升为 p3,增加了
use_count_, p3.use_count() 返回2;出了大括号,p2 被析构,use_count_ 减为1,程序末尾结束,p3 被析构,
use_count_ 减为0,X 就被析构了。
参考 :
C++ primer 第四版
Effective C++ 3rd
C++编程规范