Boost智能指针——weak_ptr

来源:互联网 发布:搜狗大数据 编辑:程序博客网 时间:2024/05/22 12:16

注意: weak_ptr在C++ 11中是可以正常使用的!!!!!!!!!!!!!!!!!!!!!!!

这是一篇有干货的好文,weak_ptr相当于一个监视的作用,监视对应的shared_ptr的情况,但是自身并不会增加对应shared_ptr的计数器!!!

所以,在循环引用的时候,被weak_ptr监视的那个ARC为1被释放后,自身的use_count()虽然之前为2,但是随着weak_ptr指向的shared_ptr被释放,自身的use_count()变成了1,最终得以释放。系统一定是先监视了所有use_count() 为1 的进行释放,然后再释放其他。。。

在objective-c中的ARC的weak是在运行时实现的!!!其他内容ARC会保证编译的时候有一些提示。。。


weak_ptr放在 #include <memory>

</pre><pre code_snippet_id="350362" snippet_file_name="blog_20140517_1_8045587" name="code" class="cpp">#include <iostream>#include <stdio.h>#include <memory>using namespace std;class parent;class children;typedef shared_ptr<parent> parent_ptr;typedef shared_ptr<children> children_ptr;class parent{public:  ~parent() { std::cout <<"destroying parent\n"; }public:  children_ptr children;};/*class children{public:  ~children() { std::cout <<"destroying children\n"; }public:  parent_ptr parent;};*/class children{public:  ~children() { std::cout <<"destroying children\n"; }public:  weak_ptr<parent> parent;};void test(){  parent_ptr father(new parent());  children_ptr son(new children());  father->children = son;  son->parent = father;}int main(){  std::cout<<"begin test...\n";  test();  std::cout<<"end test.\n";  return 0;}



循环引用:

引用计数是一种便利的内存管理机制,但它有一个很大的缺点,那就是不能管理循环引用的对象。一个简单的例子如下:

#include <string>
#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>

class parent;
class children;

typedef boost::shared_ptr<parent> parent_ptr;
typedef boost::shared_ptr<children> children_ptr;

class parent
{
public:
    ~parent() { std::cout <<"destroying parent\n"; }

public:
    children_ptr children;
};

class children
{
public:
    ~children() { std::cout <<"destroying children\n"; }

public:
    parent_ptr parent;
};


void test()
{
    parent_ptr father(new parent());
    children_ptr son(new children);

    father->children = son;
    son->parent = father;
}

void main()
{
    std::cout<<"begin test...\n";
    test();
    std::cout<<"end test.\n";
}

运行该程序可以看到,即使退出了test函数后,由于parent和children对象互相引用,它们的引用计数都是1,不能自动释放,并且此时这两个对象再无法访问到。这就引起了c++中那臭名昭著的内存泄漏

一般来讲,解除这种循环引用有下面有三种可行的方法:

  1. 当只剩下最后一个引用的时候需要手动打破循环引用释放对象。
  2. 当parent的生存期超过children的生存期的时候,children改为使用一个普通指针指向parent。
  3. 使用弱引用的智能指针打破这种循环引用。

虽然这三种方法都可行,但方法1和方法2都需要程序员手动控制,麻烦且容易出错。这里主要介绍一下第三种方法和boost中的弱引用的智能指针boost::weak_ptr。

强引用和弱引用

一个强引用当被引用的对象活着的话,这个引用也存在(就是说,当至少有一个强引用,那么这个对象就不能被释放)。boost::share_ptr就是强引用。

相对而言,弱引用当引用的对象活着的时候不一定存在。仅仅是当它存在的时候的一个引用。弱引用并不修改该对象的引用计数,这意味这弱引用它并不对对象的内存进行管理,在功能上类似于普通指针,然而一个比较大的区别是,弱引用能检测到所管理的对象是否已经被释放,从而避免访问非法内存。

boost::weak_ptr

boost::weak_ptr<T>是boost提供的一个弱引用的智能指针,它的声明可以简化如下:

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);

        ~weak_ptr();

        T* get() const
        bool expired() const
        shared_ptr<T> lock() const;
    }; 
}

可以看到,boost::weak_ptr必须从一个boost::share_ptr或另一个boost::weak_ptr转换而来,这也说明,进行该对象的内存管理的是那个强引用的boost::share_ptr。boost::weak_ptr只是提供了对管理对象的一个访问手段。

boost::weak_ptr除了对所管理对象的基本访问功能(通过get()函数)外,还有两个常用的功能函数:expired()用于检测所管理的对象是否已经释放;lock()用于获取所管理的对象的强引用指针。

通过boost::weak_ptr来打破循环引用

由于弱引用不更改引用计数,类似普通指针,只要把循环引用的一方使用弱引用,即可解除循环引用。对于上面的那个例子来说,只要把children的定义改为如下方式,即可解除循环引用:

class children
{
public:
    ~children() { std::cout <<"destroying children\n"; }

public:
    boost::weak_ptr<parent> parent;
};

最后值得一提的是,虽然通过弱引用指针可以有效的解除循环引用,但这种方式必须在程序员能预见会出现循环引用的情况下才能使用,也可以是说这个仅仅是一种编译期的解决方案,如果程序在运行过程中出现了循环引用,还是会造成内存泄漏的。因此,不要认为只要使用了智能指针便能杜绝内存泄漏。毕竟,对于C++来说,由于没有垃圾回收机制,内存泄漏对每一个程序员来说都是一个非常头痛的问题。



weak_ptr是为配合shared_ptr而引入的一种智能指针,它更像是shared_ptr的一个助手,而不是智能指针,因为它不具有普通指针的行为,没有重载operator*和operator->,它的最大作用在于协助shared_ptr,像旁观者那样观测资源的使用情况。

 

类摘要:

[cpp] view plaincopy
  1. template<class T> class weak_ptr{  
  2.      public:  
  3.      weak_ptr();  
  4.      template<class Y> weak_ptr(shared_ptr<Y> const & r);  
  5.      weak_ptr(weak_ptr const & r);  
  6.        
  7.      ~weak_ptr();  
  8.      weak_ptr & operator=(weak_ptr const &r);  
  9.        
  10.      long use_count() const;  
  11.      bool expired() const;  
  12.      shared_ptr<T> lock() const;  
  13.        
  14.      void reset();  
  15.      void swap(weak_ptr<T> &b);     
  16. };  


weak_ptr是一个“弱”指针,但它能够完成一些特殊的工作,足以证明它的存在价值。

weak_ptr被设计为与shared_ptr共同工作,可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。同样,在weak_ptr析构时也不会导致引用计数的减少,它只是一个静静地观察者。

使用weak_ptr的成员函数use_count()可以观测资源的引用计数,另一个成员函数expired()的功能等价于use_count() == 0,但更快,表示观测的资源(也就是shared_ptr管理的资源)已经不复存在了。

weak_ptr 没有重载operator*和->,这是特意的,因为它不共享指针,不能操作资源,这是它弱的原因。但它可以使用一个非常重要的成员函数lock()从被观测的shared_ptr获得一个可用的shared_ptr对象,从而操作资源。当expired() == true的时候,lock()函数将返回一个存储空指针的shared_ptr。

 

使用示例:

[cpp] view plaincopy
  1. int main(){  
  2.    shared_ptr<int> sp(new int(10));  
  3.    assert(sp.use_count() == 1);  
  4.    //create a weak_ptr from shared_ptr  
  5.    weak_ptr<int> wp(sp);  
  6.    //not increase the use count  
  7.    assert(sp.use_count() == 1);  
  8.    //judge wp is invalid  
  9.    //expired() is equivalent with use_count() == 0  
  10.    if(!wp.expired()){  
  11.       shared_ptr<int> sp2 = wp.lock();//get a shared_ptr  
  12.       *sp2 = 100;  
  13.       assert(wp.use_count() == 2);  
  14.       cout << *sp2 << endl;  
  15.    }//out of scope,sp2 destruct automatically,use_count()--;  
  16.    assert(wp.use_count() == 1);  
  17.    sp.reset();//shared_ptr is invalid  
  18.    assert(wp.expired());  
  19.    assert(!wp.lock());  
  20. }  


获得this的shared_ptr

weak_ptr的一个重要用途是获得this指针的shared_ptr,使对象自己能够生产shared_ptr管理自己:对象使用weak_ptr观测this指,这并不影响引用计数,在需要的时候就调用lock()函数,返回一个符合要求的shared_ptr使外界使用。

这个解决方案被实现为一个惯用法,在头文件<booost/enable_shared_from_this.hpp>定义了一个助手类enable_shared_from_this<T>,其声明如下:

[cpp] view plaincopy
  1. template<class T>  
  2. class enable_shared_from_this  
  3. {  
  4. public:  
  5.    shared_ptr<T> shared_from_this();  
  6. }  


使用的时候只需要让想被shared_ptr管理的类从它继承即可,成员函数shared_from_this()会返回this的shared_ptr

使用示例:

[cpp] view plaincopy
  1. #include <iostream>  
  2. #include <boost/smart_ptr.hpp>  
  3. #include <boost/enable_shared_from_this.hpp>  
  4. #include <boost/make_shared.hpp>  
  5. using namespace boost;  
  6. using namespace std;  
  7. class self_shared:  
  8. public enable_shared_from_this<self_shared>{  
  9.     public:  
  10.     self_shared(int n):x(n){}  
  11.     int x;  
  12.     void print(){  
  13.     cout << "self_shared:" << x << endl;  
  14.     }  
  15. };  
  16. int main(){  
  17.     shared_ptr<self_shared> sp =   
  18.                            make_shared<self_shared>(315);  
  19.     sp->print();  
  20.     shared_ptr<self_shared> p = sp->shared_from_this();  
  21.     p->x = 100;  
  22.     p->print();    
  23.   
  24. }  


运行结果:

self_shared:315
self_shared:100

 

需要注意的是千万不能从一个普通对象(非shared_ptr)使用shared_from_this ()获取shared_ptr,如

self_shared ss;

shaerd_ptr<self_shared> p = ss.shared_from_this();//error

 

这样虽然语法上能通过,编译也无问题,但在运行时会导致shared_ptr析构时企图删除一个栈上分配的对象,发生未定义行为。


0 0