template<class Y, class D> shared_ptr(Y * p, D d);d是deleter。如
shared_ptr<FILE> ctx(fp,::flose);


Introduction

The ​​shared_ptr​​ class template stores a pointer to a dynamically allocated object, typically with a C++ new-expression. The object pointed to is guaranteed to be deleted when the last ​​shared_ptr​​ pointing to it is destroyed or reset.

Example:
shared_ptr<X> p1( new X );
shared_ptr<void> p2( new int(5) );

​shared_ptr​​ deletes the exact pointer that has been passed at construction time, complete with its original type, regardless of the template parameter. In the second example above, when ​​p2​​ is destroyed or reset, it will call ​​delete​​ on the original ​​int*​​ that has been passed to the constructor, even though ​​p2​​ itself is of type ​​shared_ptr<void>​​ and stores a pointer of type ​​void*​​.

Every ​​shared_ptr​​ meets the ​​CopyConstructible​​, ​​MoveConstructible​​, ​​CopyAssignable​​ and ​​MoveAssignable​​ requirements of the C++ Standard Library, and can be used in standard library containers. Comparison operators are supplied so that ​​shared_ptr​​ works with the standard library's associative containers.

Because the implementation uses reference counting, cycles of ​​shared_ptr​​ instances will not be reclaimed. For example, if ​​main()​​ holds a ​​shared_ptr​​ to ​​A​​, which directly or indirectly holds a ​​shared_ptr​​ back to ​​A​​, ​​A​​'s use count will be 2. Destruction of the original ​​shared_ptr​​ will leave ​​A​​ dangling with a use count of 1. Use ​​weak_ptr​​ to "break cycles."

The class template is parameterized on ​​T​​, the type of the object pointed to. ​​shared_ptr​​ and most of its member functions place no requirements on ​​T​​; it is allowed to be an incomplete type, or ​​void​​. Member functions that do place additional requirements (​​constructors​​, ​​reset​​) are explicitly documented below.

​shared_ptr<T>​​ can be implicitly converted to ​​shared_ptr<U>​​ whenever ​​T*​​ can be implicitly converted to ​​U*​​. In particular, ​​shared_ptr<T>​​ is implicitly convertible to ​​shared_ptr<T const>​​, to ​​shared_ptr<U>​​ where ​​U​​ is an accessible base of ​​T​​, and to ​​shared_ptr<void>​​.

​shared_ptr​​ is now part of the C++11 Standard, as ​​std::shared_ptr​​.

Starting with Boost release 1.53, ​​shared_ptr​​ can be used to hold a pointer to a dynamically allocated array. This is accomplished by using an array type (​​T[]​​ or ​​T[N]​​) as the template parameter. There is almost no difference between using an unsized array, ​​T[]​​, and a sized array, ​​T[N]​​; the latter just enables ​​operator[]​​ to perform a range check on the index.

Example:
shared_ptr<double[1024]> p1( new double[1024] );
shared_ptr<double[]> p2( new double[n] );

Best Practices

A simple guideline that nearly eliminates the possibility of memory leaks is: always use a named smart pointer variable to hold the result of ​​new​​. Every occurence of the ​​new​​keyword in the code should have the form:

shared_ptr<T> p(new Y);


It is, of course, acceptable to use another smart pointer in place of ​​shared_ptr​​ above; having ​​T​​ and ​​Y​​ be the same type, or passing arguments to ​​Y​​'s constructor is also OK.

If you observe this guideline, it naturally follows that you will have no explicit ​​delete​​ statements; ​​try/catch​​ constructs will be rare.

Avoid using unnamed ​​shared_ptr​​ temporaries to save typing; to see why this is dangerous, consider this example:

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


The function ​​ok​​ follows the guideline to the letter, whereas ​​bad​​ constructs the temporary ​​shared_ptr​​ in place, admitting the possibility of a memory leak. Since function arguments are evaluated in unspecified order, it is possible for ​​new int(2)​​ to be evaluated first, ​​g()​​ second, and we may never get to the ​​shared_ptr​​constructor if ​​g​​ throws an exception. See ​​Herb Sutter's treatment​​ (also ​​here​​) of the issue for more information.

The exception safety problem described above may also be eliminated by using the ​​make_shared​​ or ​​allocate_shared​​ factory functions defined in ​​boost/make_shared.hpp​​. These factory functions also provide an efficiency benefit by consolidating allocations.

-------------------------------------------------------------------------------------------------------

boost::scoped_ptr虽然简单易用,但它不能共享所有权的特性却大大限制了其使用范围,而boost::shared_ptr可以解决这一局限。顾名思义,boost::shared_ptr是可以共享所有权的智能指针,首先让我们通过一个例子看看它的基本用法:



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

using namespace std;

using boost::shared_ptr;

class A
{
public:
~A(){ cout<<"destroy \n";}
void do_sth(){cout<<"do somthing\n";}
};

int main()
{
shared_ptr<A> a1(new A());
cout<<"the sample now has "<<a1.use_count()<<" references \n";

shared_ptr<A> a2=a1;
cout<<"the sample now has "<<a1.use_count()<<" references \n";

a1.reset();
std::cout<<"After Reset sp1. The Sample now has "<<a2.use_count()<<" references\n";

a2.reset();
std::cout<<"After Reset sp2.\n";
}


root@iZ23onhpqvwZ:~/ms/boost# ./shared_ptr

the sample now has 1 references

the sample now has 2 references

After Reset sp1. The Sample now has 1 references

destroy

After Reset sp2.

可以看到,boost::shared_ptr指针sp1和sp2同时拥有了implementation对象的访问权限,且当sp1和sp2都释放对该对象的所有权时,其所管理的的对象的内存才被自动释放。在共享对象的访问权限同时,也实现了其内存的自动管理。

boost::shared_ptr的特点:

和前面介绍的boost::scoped_ptr相比,boost::shared_ptr可以共享对象的所有权,因此其使用范围基本上没有什么限制(还是有一些需要遵循的使用规则,下文中介绍),自然也可以使用在stl的容器中。另外它还是线程安全的,这点在多线程程序中也非常重要。

 细节:shared_ptr实现了operator bool() const方法来判断一个管理的资源是否被释放

 

boost::shared_ptr的使用规则:

boost::shared_ptr并不是绝对安全,下面几条规则能使我们更加安全的使用boost::shared_ptr:

  1. 避免对shared_ptr所管理的对象的直接内存管理操作,以免造成该对象的重释放
  2. shared_ptr并不能对循环引用的对象内存自动管理(这点是其它各种引用计数管理内存方式的通病)。
  3. 不要构造一个临时的shared_ptr作为函数的参数。
    如下列代码则可能导致内存泄漏:
    void test()
    {
        foo(boost::shared_ptr<implementation>(new    implementation()),g());
    }
    正确的用法为:
    void test()
    {
        boost::shared_ptr<implementation> sp    (new implementation());
        foo(sp,g());
    }

         

当函数g()抛异常的时候就会泄露了,这个是boost文档上特地注明的标准bad Practices。

 

---------------------

shared_ptr陷阱:


条款1:不要把一个原生指针给多个shared_ptr管理


int* ptr = new int;


shared_ptr<int> p1(ptr);


shared_ptr<int> p2(ptr); //logic error


ptr对象被删除了2次


这种问题比喻成“二龙治水”,在原生指针中也同样可能发生。


 


条款2:不要把this指针给shared_ptr


class Test{


public:


    void Do(){  m_sp =  shared_ptr<Test>(this);  }


private:


    shared_ptr<Test> m_member_sp;


};


 


Test* t = new Test;


shared_ptr<Test> local_sp(t);


p->Do();


 


发生什么事呢,t对象被删除了2次!


t对象给了local_sp管理,然后在m_sp =  shared_ptr<Test>(this)这句里又请了一尊神来管理t。


这就发生了条款1里“二龙治水”错误。


 


条款3:shared_ptr作为被保护的对象的成员时,小心因循环引用造成无法释放资源。


 


对象需要相互协作,对象A需要知道对象B的地址,这样才能给对象B发消息(或调用其方法)。


设计模式中有大量例子,一个对象中有其他对象的指针。现在把原生指针替换为shared_ptr.


 


假设a对象中含有一个shared_ptr<B>指向b对象;假设b对象中含有一个shared_ptr<A>指向a对象


并且a,b对象都是堆中分配的。很轻易就能与他们失去最后联系。


考虑某个shared_ptr<A> local_a;是我们能最后一个看到a对象的共享智能指针,其use_count==2,


因为对象b中持有a的指针。所以当local_a说再见时,local_a只是把a对象的use_count改成1。


同理b对象。然后我们再也看不到a,b的影子了,他们就静静的躺在堆里,成为断线的风筝。


 


解决方案是:Use weak_ptr to "break cycles."(boost文档里写的)或者显示的清理


 


条款4:不要在函数实参里创建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());  //Boost推荐写法


 


条款5:对象内部生成shared_ptr


 


前面说过,不能把this指针直接扔给shared_ptr. 但是没有禁止在对象内部生成自己的shared_ptr


 


//这是Boost的例子改的。


class Y: public boost::enable_shared_from_this<Y>


{


    boost::shared_ptr<Y> GetSelf()


    {


        return shared_from_this();


    }


};


 


原理是这样的。普通的(没有继承enable_shared_from_this)类T的shared_ptr<T> p(new T).


p作为栈对象占8个字节,为了记录(new T)对象的引用计数,p会在堆上分配16个字节以保存


引用计数等“智能信息”。share_ptr没有“嵌入(intrusive)”到T对象,或者说T对象对share_ptr毫不知


 


情。Y对象则不同,Y对象已经被“嵌入”了一些share_ptr相关的信息,目的是为了找到“全局性”的


那16字节的本对象的“智能信息”。


 


原理说完了,就是陷阱


Y y;


boost::shared_ptr<Y> p=  y.GetSelf(); //无知的代码,y根本就不是new出来的


 


Y* y = new Y;


boost::shared_ptr<Y> p=  y->GetSelf(); //似是而非,仍旧程序崩盘。


Boost文档说,在调用shared_from_this()之前,必须存在一个正常途径创建的shared_ptr


 


boost::shared_ptr<Y> spy(new Y)


boost::shared_ptr<Y> p =  spy->GetSelf(); //OK


 


---使用例子:




#include<boost/enable_shared_from_this.hpp>
#include<boost/make_shared.hpp>
#include<iostream>
using namespace std;
using namespace boost;

class A:public enable_shared_from_this<A>
{
public:
A(int n):x(n){}
void print(){ cout<<"T:"<<x<<endl;}

int x;
};
int main()
{
shared_ptr<A> sp=make_shared<A>(111);
sp->print();

shared_ptr<A> p=sp->shared_from_this();
p->x=1000;
p->print();

sp->print();
}


注意:千万不能从一个普通对象(非shared_ptr)使用shared_from_this()获去shared_ptr,例如

self_shared ss;

shared_ptr<self_shared> p = ss.shared_from_this();//错误!

在运行时会导致shared_ptr析构时企图删除一个栈上分配的对象,发生未定义行为。


条款6 :处理不是new的对象要小心。


 


int* pi = (int*)malloc(4)


shared_ptr<int> sp( pi ) ; //delete马嘴不对malloc驴头。


 


条款7:多线程对引用计数的影响。


 


如果是轻量级的锁,比如InterLockIncrement等,对程序影响不大


如果是重量级的锁,就要考虑因为share_ptr维护引用计数而造成的上下文切换开销。



1.33版本以后的shared_ptr对引用计数的操作使用的是Lock-Free(类似InterLockIncrement函数族)


的操作,应该效率不错,而且能保证线程安全(库必须保证其安全,程序员都没有干预这些隐藏事物的机会)。


Boost文档说read,write同时对shared_ptr操作时,行为不确定。这是因为shared_ptr本身有两个成员px,pi。


多线程同时对px读写是要出问题的。与一个int的全局变量多线程读写会出问题的原因一样。


 


条款8:对象数组用shared_array


 


int* pint = new int[100];


shared_array<int> p (pint );


 


既然shared_ptr对应着delete;显然需要一个delete[]对应物shared_array


 


条款9:学会用删除器


 


struct Test_Deleter


{   


    void  operator ()( Test* p){   ::free(p);   }


};


Test* t = (Test*)malloc(sizeof(Test));


new (t) Test;


 


shared_ptr<Test> sp( t ,  Test_Deleter() ); //删除器可以改变share_ptr销毁对象行为


 


有了删除器,shared_array无用武之地了。


template<class T>


struct Array_Deleter


{   


    void  operator ()( T*){   delete[] p;   }


};


int* pint = new int[100];


shared_ptr<int> p (pint, Array_Deleter<int>() );


 


条款10:学会用分配器


 


存放引用计数的地方是堆内存,需要16-20字节的开销。


如果大量使用shared_ptr会造成大量内存碎片。


shared_ptr构造函数的第3个参数是分配器,可以解决这个问题。


 


shared_ptr<Test> p( (new Test), Test_Deleter(), Mallocator<Test>() );


注意删除器Test_Deleter是针对Test类的。分配器是针对shared_ptr内部数据的。


 


Mallocator<Test>()是个临时对象(无状态的),符合STL分配器规约。


 


template <typename T> 


class Mallocator { 


    //略。。。。。。


    T * allocate(const size_t n) const {


        return singleton_pool<T,sizeof(T)>::malloc();


    }


    //略。。。。。。


 


Mallocator传入Test,实际分配的类型确是


class boost::detail::sp_counted_impl_pda<class Test *,


                                         struct Test_Deleter,


                                         class Mallocator<class Test> >


这是用typeid(T).name()打印出来的。可能和rebind相关。


 



条款11 weak_ptr在使用前需要检查合法性。


weak_ptr<K> wp;


{


shared_ptr<K>  sp(new K);  //sp.use_count()==1


wp = sp; //wp不会改变引用计数,所以sp.use_count()==1


shared_ptr<K> sp_ok = wp.lock(); //wp没有重载->操作符。只能这样取所指向的对象


}


shared_ptr<K> sp_null = wp.lock(); //sp_null .use_count()==0;


因为上述代码中sp和sp_ok离开了作用域,其容纳的K对象已经被释放了。


得到了一个容纳NULL指针的sp_null对象。在使用wp前需要调用wp.expired()函数判断一下。


因为wp还仍旧存在,虽然引用计数等于0,仍有某处“全局”性的存储块保存着这个计数信息。


直到最后一个weak_ptr对象被析构,这块“堆”存储块才能被回收。否则weak_ptr无法直到自己


所容纳的那个指针资源的当前状态。


 


条款12 不要new shared_ptr<T>


 


本来shared_ptr就是为了管理指针资源的,不要又引入一个需要管理的指针资源shared_ptr<T>*


 


条款13  尽量不要get


 


class B{...};


class D : public B{ ...};  //继承层次关系


 


shared_ptr<B> sp (new D);    //通过隐式转换,储存D的指针。


B* b = sp.get();             //shared_ptr辛辛苦苦隐藏的原生指针就这么被刨出来了。


D* d = dynamic_cast<D*>(b);  //这是使用get的正当理由吗?


 


正确的做法


shared_ptr<B> spb (new D)  ;


shared_ptr<D> spd = shared_dynamic_cast<D>(spb); //变成子类的指针


shared_ptr在竭尽全力表演的像一个原生指针,原生指针能干的事,它也基本上能干。


 


另一个同get相关的错误


shared_ptr<T> sp(new T);


shared_ptr<T> sp2( sp.get() ) ;//又一个“二龙治水”实例,指针会删2次而错误。


 


条款14 不要memcpy shared_ptr


 


shared_ptr<B> sp1 (new B)  ;


shared_ptr<B> sp2;


memcpy(&sp2,&sp1,sizeof(shared_ptr<B>)); //sp2.use_count()==1


很显然,不是通过正常途径(拷贝构造,赋值运算),引用计数是不会正确增长的。


 


条款15 使用BOOST预定义的宏去改变shared_ptr行为。


 


shared_ptr行为由类似BOOST_SP_DISABLE_THREADS这样的宏控制。需要去学习他们到底是干什么的。


大师Andrei Alexandrescu设计了一种基于模板策略设计模式的智能指针,通过几个模板参数去定制化


智能指针的行为。Boost却不以为然,官方解释是:需要统一的接口,这样利于大规模书写。


smart_ptr<T,OwnershipPolicy,ConversionPolicy,CheckingPolicy,StoragePolicy> sp(new T);


上述接口缺点是外形复杂,看上去像个大花脸。优点是客户程序员可以轻易的定制行为。


 


条款17 构造函数里调用shared_from_this抛例外


 


class Holder:public enable_shared_from_this<Holder>{


public:


    Holder() {


        shared_ptr<Holder> sp = shared_from_this();


        int x = sp.use_count();


    }


};


同前面条款5,不符合enable_shared_from_this使用前提。


 


总结:


学习了一天就总结出10多条条款,长期研究一下恐怕就出现条款100了。为什么还要使用shared_ptr呢?


有很多开源库用shared_ptr,而且shared_ptr具有“传染性”(某网友语:像毒品沾上就甩不掉),


抛开它就会有更严重的多龙治水现象。shared_ptr作为原生指针的替代品,能解决一定的内存泄露问题。


实际上初学原生指针时,每个人都遇到过野指针,删两次,忘记删除等问题。学习shared_ptr也会遇到。


shared_ptr的确能改善上述问题,并不能完全解决问题。shared_ptr可能在将来占主流,它最可能号令江湖,


否则一大堆auto_ptr,weak_ptr,原生指针,scoped_ptr共存就把人搞糊涂了。


 


-----------------------------------------------------------


智能指针总结:



C++中没有垃圾回收,所有的内存都要自己管理,也就是说new 与 delete必须由程序员自己去一一对应起来。这个难免会出现这样那样的内存问题。在这种背景下,智能指针应运而生。

智能指针的最终的目的是将一块内存交给一个托管对象,内存何时释放由托管对象来决定,而对对象的访问也使用托管对象来实现。这样子对象new完后就不需要显示的delete了,在它的生命周期结束后(也就是没有托管对象引用它的情况下)就可以自动被释放掉了。

智能指针有些比较简单,如 std::auto_ptr , boost::scope_ptr等等,它们的功能都比较类似,就是在某一个区域内指向一个new出来的对象,当这个指针的生命周期结束后,在析构函数中调用delete函数释放这块内存,这类智能指针充其量就是少写了一个delete而已,由于它不能被传递到该指针生命周期外的其它的地方使用,故没什么太大的用处。

 

稍微复杂一些的智能指针都是维护是引用记数器的智能指针,这种指针可以任意传递,用法基本上和原生指针类似,所以使用的比较多一些。

带引用记数的智能指针原理上基本类似,就是new了某一个资源的时候,给该资源加上一个计数器,每当有新的智能指针指向这个资源的时候,计数器就加1,当某个指向该资源智能指针超出生命周期或由于其它原因解除了和该资源的引用关系时,计数器减1,当计数器减到0 的时候,表示该资源已经没有指针引用,就可以被释放了。

 

根据记数器所处的位置不同,基本引用记数器的智能指针又分为两类,一类是计数器处于资源中,一类是记数器处于智能指针中。

 

引用计数器处于资源中的智能指针管理的资源必须从某一个基类继承下来,在这个基类中提供了addref 与 release方法来管理引用计数。这样子智能指针通过调用资源对象的addref 与 release 方法来实现资源的计数管理。很多类库或系统的框架都会让所有的对象从一个基类中继承,而引用计数功能就可以放在这个基类中实现。这种智能指针是专用的,也就是说它不能管理不从这个基类继承的对象。

 

 

另外一种方法是将引用记数器放在智能指针本身中,这样子智能指针就可以管理任意new出来的对象的生命周期,而不会对象本身作任何限制。这类智能指针的基本原理是每当一个智能指针被一个原生指针初始化的时候,就new出来一个引用记数器。当指针被传递的时候,这个引用记数器中的值被修改,而引用记数器本身的地址也被传递给其它的智能指针,这种技术的关键在于,智能指针中保存的不同引用计数器本身,而是引用计数器的一个地址,这样就可以实现同一个资源的多个智能指针的传递过程中能保证引用记数的正确。最后,智能指针引用的对象的释放与引用记数器本身的释放是同步的,即一个资源的引用对象为0的时候,它就可以被删除了,同时,这个引用对象也可以被删除了,因为一个引用对象就是绑定到一个特写资源的,引用对象本身不对重用。这种类型的智能指针代表是boost::share_ptr。

 

 

基于引用计数的智能指针一般都会存在循引用的问题,关于循环引用这里兴个例子(代码是网上拷的,见谅)

 

【1】boost::weak_ptr简介

boost::weak_ptr属于boost库,定义在namespace boost中,包含头文件 #include<boost/weak_ptr.hpp>便可以使用。

【2】boost::weak_ptr详解

智能指针boost::scope_ptr和智能指针boost::shared_ptr就完全可以解决所有单个对象内存的管理问题。

这儿咋还多出一个boost::weak_ptr,是否还有某些案例我们没有考虑到呢?

回答:有。首先 boost::weak_ptr是专门为boost::shared_ptr而准备的。

有时候,我们只关心能否使用对象,并不关心内部的引用计数。

boost::weak_ptr是 boost::shared_ptr的观察者(Observer)对象,观察者?观察者怎么理解呢?

观察者意味着boost::weak_ptr只对boost::shared_ptr进行引用,而不改变其引用计数。

当被观察的boost::shared_ptr失效后,相应的boost::weak_ptr也随之失效。

那么,为什么需要这个观察者呢?那还得从循环引用谈起。

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

示例代码如下:

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

   std::cout<<father.use_count()<<std::endl;//输出2

}


void main()

{

    std::cout<<"begin test.../n";

    test();

    std::cout<<"end test./n";

}

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

 改成weak_ptr,输出1.

 

为了解决这种情况,就出现了另一个概念,叫弱引用,弱引用指针从一个强引用指针产生,弱引用不增加被引用对象的引用计数,并提供了相应的函数来判断当前引用的对象是否已经过期被释放。如boost::weak_ptr为是一个指向boost::share_ptr的弱引用。

 

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

1. 当只剩下最后一个引用的时候需要手动打破循环引用释放对象。

2. 当parent的生存期超过children的生存期的时候,children改为使用一个普通指针指向parent。

3. 使用弱引用的智能指针打破这种循环引用。

虽然这三种方法都可行,但方法1和方法2都需要程序员手动控制,麻烦且容易出错。

下面主要介绍一下第三种方法和boost中的弱引用的智能指针boost::weak_ptr。 

<2>什么是强引用和弱引用?

一个强引用是指当被引用的对象仍活着的话,这个引用也存在(也就是说,只要至少有一个强引用,那么这个对象就不会也不能被释放)。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。

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

)

boost::weak_ptr只是提供了对管理对象的一个访问手段。

boost::weak_ptr除了对所管理对象的基本访问功能(通过get()函数)外,还有两个常用的功能函数:

1. expired() 用于检测所管理的对象是否已经释放;

2. lock() 用于获取所管理的对象的强引用指针。  weak_ptr可以使用一个非常重要的成员函数lock()从被观测的shared_ptr获得一个可用的shared_ptr对象, 从而操作资源。但当expired()==true的时候,lock()函数将返回一个存储空指针的shared_ptr.

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

由于弱引用不更改引用计数,类似普通指针,只要把循环引用的一方使用弱引用,即可解除循环引用。

对于上面的那个例子来说,只要把children的定义改为如下方式,即可解除循环引用



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

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


(还有parent这里变量名和class parent重复,不行,改成parentx就可以了)

【3】boost::weak_ptr总结

虽然通过弱引用指针可以有效的解除循环引用,但这种方式必须在程序员能预见会出现循环引用的情况下才能使用,也可以是说这个仅仅是一种编译期的解决方案。

如果程序在运行过程中出现了循环引用,还是会造成内存泄漏的。

因此,不要认为只要使用了智能指针便能杜绝内存泄漏。

毕竟,对于C++来说,由于没有垃圾回收机制,内存泄漏对每一个程序员来说都是一个非常头痛的问题。 

一篇关于weak_ptr小短文:

近期在补充和梳理C++方面的知识的时候,遇到了WeakPtr这个概念和用法,不甚明白,Google出了一堆文字,包括Boost的shared_ptr和weak_ptr的比较,以及其他一些博客里面给的例子以及解释。

其中有一个是这样解释的,原文大意:在C++的weak_ptr里面,有两类指针,一类是拥有对象的生命周期;另一类不拥有,只有访问权。

在这里,我就有点不明白,这两类指针,到底会在编写上面,或者形式上面,有什么区别呢?

或者说weak_ptr到底要解决编程当中的什么问题呢? 为什么会有这个类型被提出?

经过和一位朋友的讨论,有了答案。

比如现在有两个函数

class A;

void Func1(A* a);

void Func2(A* a);

 

A* a = new A;

 

Func1(a);

Func2(a);

 

他们在同一个作用域的时候,a 被释放后,惯有的操作是 a = NULL这个时候Func2 或者 Func1中都可以判断到这个变化。

但是如果Func1在另外一个线程/进程里面,你在传递过来的时候a并不是空的a,但是指向的对象,已经被释放了,这个指针已经不在有权限去访问了。那么就会出现问题。

 

所以weak_ptr就出现了,让你在使用资源之前,先去问下HasWeakPtr() 如果有,那么表明这个资源还没有被释放,可以放心去用,否则就应该不能再使用了。

 

以往的代码,写法: if (a) {} 

但是*a 已经被释放之后,其实a并没有在上下文被a  = NULL 所以a 还是指向那个地址,但是地址指向的内容已经非法了。

 eof

    在std::shared_ptr被引入之前,C++标准库中实现的用于管理资源的智能指针只有std::auto_ptr一个而已。std::auto_ptr的作用非常有限,因为它存在被管理资源的所有权转移问题。这导致多个std::auto_ptr类型的局部变量不能共享同一个资源,这个问题是非常严重的哦。因为,我个人觉得,智能指针内存管理要解决的根本问题是:一个堆对象(或则资源,比如文件句柄)在被多个对象引用的情况下,何时释放资源的问题。何时释放很简单,就是在最后一个引用它的对象被释放的时候释放它。关键的问题在于无法确定哪个引用它的对象是被最后释放的。std::shared_ptr确定最后一个引用它的对象何时被释放的基本想法是:对被管理的资源进行引用计数,当一个shared_ptr对象要共享这个资源的时候,该资源的引用计数加1,当这个对象生命期结束的时候,再把该引用技术减少1。这样当最后一个引用它的对象被释放的时候,资源的引用计数减少到0,此时释放该资源。下边是一个shared_ptr的用法例子:

 




[cpp] ​​view plain​​ ​​copy​

 

 ​​print​​​​?​

  1. #include <iostream>  
  2. #include <memory>  
  3.   
  4. class Woman;  
  5. class Man{  
  6. private:  
  7.     std::weak_ptr<Woman> _wife;  
  8.     //std::shared_ptr<Woman> _wife;  
  9. public:  
  10.     void setWife(std::shared_ptr<Woman> woman){  
  11.         _wife = woman;  
  12.     }  
  13.   
  14.     void doSomthing(){  
  15.         if(_wife.lock()){  
  16.         }  
  17.     }  
  18.   
  19.     ~Man(){  
  20.         std::cout << "kill man\n";  
  21.     }  
  22. };  
  23.   
  24. class Woman{  
  25. private:  
  26.     //std::weak_ptr<Man> _husband;  
  27.     std::shared_ptr<Man> _husband;  
  28. public:  
  29.     void setHusband(std::shared_ptr<Man> man){  
  30.         _husband = man;  
  31.     }  
  32.     ~Woman(){  
  33.         std::cout <<"kill woman\n";  
  34.     }  
  35. };  
  36.   
  37.   
  38. int main(int argc, char** argv){  
  39.     std::shared_ptr<Man> m(new Man());  
  40.     std::shared_ptr<Woman> w(new Woman());  
  41.     if(m && w) {  
  42.         m->setWife(w);  
  43.         w->setHusband(m);  
  44.     }  
  45.     return 0;  
  46. }  


    在Man类内部会引用一个Woman,Woman类内部也引用一个Man。当一个man和一个woman是夫妻的时候,他们直接就存在了相互引用问题。man内部有个用于管理wife生命期的shared_ptr变量,也就是说wife必定是在husband去世之后才能去世。同样的,woman内部也有一个管理husband生命期的shared_ptr变量,也就是说husband必须在wife去世之后才能去世。这就是循环引用存在的问题:husband的生命期由wife的生命期决定,wife的生命期由husband的生命期决定,最后两人都死不掉,违反了自然规律,导致了内存泄漏。

 

     解决std::shared_ptr循环引用问题的钥匙在weak_ptr手上。weak_ptr对象引用资源时不会增加引用计数,但是它能够通过lock()方法来判断它所管理的资源是否被释放。另外很自然地一个问题是:既然weak_ptr不增加资源的引用计数,那么在使用weak_ptr对象的时候,资源被突然释放了怎么办呢?呵呵,答案是你根本不能直接通过weak_ptr来访问资源。那么如何通过weak_ptr来间接访问资源呢?答案是:在需要访问资源的时候weak_ptr为你生成一个shared_ptr,shared_ptr能够保证在shared_ptr没有被释放之前,其所管理的资源是不会被释放的。创建shared_ptr的方法就是lock()方法

    细节:shared_ptr实现了operator bool() const方法来判断一个管理的资源是否被释放