1. boost::shared_ptr的用法
下面用一个简单的例子说明shared_ptr的用法:
#include <stdio.h> #include <boost/shared_ptr.hpp> class A { public: void print() { printf("class A print!\n"); } }; int main(int argc, char **argv) { boost::shared_ptr<A> a1(new A()); a1->print(); }
shared_ptr不用手动去释放资源,它会智能地在合适的时候去自动释放。如上面的例子,a1指向的对象将会在程序结束的时候自动释放(程序结束时所有申请的资源都会被释放,这只是为了说明其作用)。再来看下面的例子:
//同上 int main(int argc, char **argv) { boost::shared_ptr<A> a1(new A()); a1->print(); printf("a1 reference count: %d\n", a1.use_count()); boost::shared_ptr<A> a2 = a1; printf("a1 reference count: %d\n", a1.use_count()); printf("a2 reference count: %d\n", a2.use_count()); a1.reset(); printf("a2 reference count: %d\n", a2.use_count()); }
程序输出结果:
class A print! a1 reference count: 1 a1 reference count: 2 a2 reference count: 2 a2 reference count: 1
上面调用了两上shared_ptr的成员方法,user_count()的作用是获得当前对象被引用的次数,reset()的作用是释放指针对对象的引用,将指针设为空。
2. boost::shared_ptr的实现机制
boost::shared_ptr的实现机制其实比较简单,就是对指针引用的对象进行引用计数,当有一个新的boost::shared_ptr指针指向一个对象时,就把该对象的引用计数加1,减少一个boost::shared_ptr指针指向一个对象时,就把对该对象的引用计数减1。当一个对象的引用计数变为0时,就会自动调用其析构函数或者free掉相应的空间。
boost::shared_ptr的常用成员函数:
(1) 构造一个空的指针
shared_ptr(); // never throws shared_ptr(std::nullptr_t); // never throws template<class D> shared_ptr(std::nullptr_t p, D d); template<class D, class A> shared_ptr(std::nullptr_t p, D d, A a);
上面几个函数可以初始化一个空的shared_ptr指针,其中,第三和第四个函数中的参数的意思是:d表示一个删除器(deleter),它会在释放资源的时候被调用,delete p会变成d(p)。a表示一个构造器,被用作分配空间。这两个接口允许调用者自己提供构造器和删除器,来自定义自己的构造和释放行为。
(2) 根据变量构造指针
template<class Y> explicit shared_ptr(Y * p); template<class Y, class D> shared_ptr(Y * p, D d); template<class Y, class D, class A> shared_ptr(Y * p, D d, A a);
这几个构造函数是通过一个Y类型的指针类型p来初始化shared_ptr指针,初始化后,指针会指针p所指的对象。其中,参数d和a的意义和上面相同。
(3) 拷贝构造函数
shared_ptr拥有常见的拷贝构造,移动构造函数,用法和普通构造函数一样,这里不做详述。还有一个比较特殊的构造函数:
template<class Y> shared_ptr(shared_ptr<Y> const & r, element_type * p); // never throws
这个函数的在boost的帮助文档中解释为:constructs a shared_ptr
that shares ownership with r
and stores p(构造一个shared_ptr对象存储p并且与r共享所有权),这个构造函数被称为
aliasing constructor(不知道如何翻译,aliasing有重叠的意思)。r是将要共享所有权的指针,p是实际指向的对象,构造的指针调用get()或者operator->将返回p,而不是r。为了更好的理解这个函数,我们考虑shared_ptr指针对象由两个部分构成,一个是它的所有权(可以与其他指针共享的),另一个是它实际存储的对象。在普通应用中,这两部分是相同的。在由上述函数构造的shared_ptr中,两个部分是不同的。当一个指针的引用计数为0时,如果它还与其他指针共享所有权,那么它实际存储的对象不会被删除,直到共享的引用计数为0。下面的例子会更直观一些。
aliasing (10) | template <class U> shared_ptr (const shared_ptr<U>& x, element_type* p) noexcept; |
---|
The object does not own p, and will not manage its storage. Instead, it co-owns x's managed object and counts as one additional use of x. It will also delete x's pointer on release (and not p).
It can be used to point to members of objects that are already managed.
struct data {...}; struct object { data data_; }; void f () { shared_ptr<object> o (new object); // use_count == 1 shared_ptr<data> d (o, &o->data_); // use_count == 2 o.reset (); // use_count == 1 // When d goes out of scope, object is deleted. } void g () { typedef std::vector<object> objects; shared_ptr<objects> os (new objects); // use_count == 1 os->push_back (object ()); os->push_back (object ()); shared_ptr<object> o1 (os, &os->at (0)); // use_count == 2 shared_ptr<object> o2 (os, &os->at (1)); // use_count == 3 os.reset (); // use_count == 2 // When o1 goes out of scope, use_count becomes 1. // When o2 goes out of scope, objects is deleted. }
3. 使用boost::shared_ptr的注意事项
(1) 不要把一个原生指针给多个shared_ptr管理
int* ptr = new int; boost::shared_ptr<int> p1(ptr); boost::shared_ptr<int> p2(ptr);
这样做会导致ptr会被释放两次。在实际应用中,保证除了第一个shared_ptr使用ptr定义之外,后面的都采用p1来操作,就不会出现此类问题。
(2) 不要在函数实参里创建shared_ptr
function(shared_ptr<int>(new int), g()); //有缺陷 //可能的过程是先new int,然后调g(),g()发生异常,shared_ptr<int>没有创建,int内存泄露 //推荐写法 shared_ptr<int> p(new int()); f(p, g());
(3) shared_ptr作为被保护的对象的成员时,小心因循环引用造成无法释放资源。
简单的例子:
class parent; class children; typedef boost::shared_ptr<parent> parent_ptr; typedef boost::shared_ptr<children> children_ptr; class parent { public: children_ptr children; }; class children {public: parent_ptr parent; }; void test() { boost::shared_ptr<parent> father( new parent); boost::shared_ptr<children> son(new children); father->children = son; //user_count() == 2 son->parent = father; //user_count() == 2 }
在这个例子中,出现了循环引用计数,赋值后use_count()变为2,出函数后变为1,资源无法被释放。boost的解决方法是采用weak_ptr来保存。
class parent {public: boost::weak_ptr<children> children; }; class children {public: boost::weak_ptr<father> parent; };
因为boost不会影响weak_ptr不会影响引用计数,不会造成循环引用计数。
(4) 不要把this指针给shared_ptr
将this指针赋给shared_ptr会出现this指针被释放两次的危险,如下面的代码,会在t释放时析构一次,shared_ptr释放时析构一次。
class test { public: boost::shared_ptr<test> pget() { return boost::shared_ptr<test>(this); } }; test t; boost::shared_ptr<test> pt = t.pget();
boost库提供的解决方法是:使用enable_shared_from_this来实现。
class test : public boost::enable_shared_from_this<test> { public: boost::shared_ptr<test> pget() { return shared_from_this(); } }; test t; boost::shared_ptr<test> pt = t.pget();
4. std::tr1::shared_ptr和boost::shared_ptr
在新版本的C++标准中引用shared_ptr智能指针,名空间是std::tr1::shared_ptr。它和boost::shared_ptr的用法相同,在gcc4.3.x及以上的版本加选项-std=gnu++0x即可使用。