vim.cpp

 


4.boost库的shared,weak,scoped ptr
    (1).scoped_ptr
        scoped_ptr的好处在于:它没有复制构造函数和赋值函数,而且在离开一个作用域的时候会自动的调用对象的析构函数,所以比较常用。
        而且scoped_ptr就相当于一个裸指针,对程序的性能没有任何的影响。
       
        ex:
        void f()
        {
            scoped_ptr<MyClass> p(new MyClass);//定义,在p离开作用域后会自动调用myClass的析构函数。
           
            scoped_ptr<MyClass> p1(p);//错误,因为没有拷贝构造函数
            scoped_ptr<MyClass> p2=p;//编译错误,没有赋值函数
        }
       
    (2).shared_ptr, weak_ptr
        模拟定义shared_ptr,和weak_ptr,下面只给出了关键数据成员的定义,没有给出成员函数的定义。
       
        class _Ref_count_base      
        {                    
            long _Uses;//这个是引用计数,也就是shared_ptr的计数            
            long _Weaks;//这个是weak_ptr的计数           
           
        };                        
                                   
        template <class T>         
        class shared_ptr           
        {                          
            _Ty *_Ptr;             
            _Ref_count_base *_Rep; 
                                   
            shared_ptr();          
            shared_ptr(T *ptr);    
            //还有其它的一些构造函数
        };                         
                                   
        template <class T>         
        class weak_ptr             
        {                          
            _Ty *_Ptr;             
            _Ref_count_base *_Rep; 
                                   
            weak_ptr();            
            //还有其它的一些构造函数
        };                         
       
        weak_ptr只有一个weak_ptr()构造函数,而没有一个像shared_ptr(T *ptr); 的单参构造函数,
        所以有效的weak_ptr只能由shared_ptr或者其他的weak_ptr转化而来。
        ex:
        std::tr1::shared_ptr<A> pShared1(new A);      
        std::tr1::weak_ptr<A> pWeak;       
        pWeak = pShared1;
       
        下面来讨论引用计数的问题,这里我们忽略_Ref_count_base的_Weaks成员,因为没有起到关键作用。
        我们在讨论引用计数前,需要知道只对引用计数的读和写是线程安全的,因为这里对引用计数采用的是原子读和写,也就是一次性读完和写完,
        不存在线程竞争的状况。(windows的原子操作是InterlockedAdd等, linux的原子操作是 atomic...)
       
        下面是shared_ptr对引用计数的影响,只有两种情况,引用计数不加1就减1.
        (1).引用计数增加的情况
       
        shared_ptr的赋值操作符,进行的是浅拷贝(也就是pShared1和pShared2的_Ptr和_Rep都指向的是同一个地址),但是会把引用计数 _Uses 增加 1.
        std::tr1::shared_ptr<A> pShared1(new A);
        std::tr1::shared_ptr<A> pShared2;
        pShared2 = pShared1;
       
        shared_ptr的拷贝构造函数 和 赋值操作符 是同样的功能。
        std::tr1::shared_ptr<A> pShared2(pShared1)
       
        注意在把 shared_ptr 作为函数的参数的时候,调用的是 拷贝构造函数。
        ex: void f(shared_ptr<A> ptr);
       
        在weak_ptr变到shared_ptr的时候引用计数会增加1.
       
       
        (2).引用计数减少的情况
       
        如上面的例子:pShared1,pShared2,ptr,在离开他们的作用域的时候,会自动调用shared_ptr的析构函数,这是引用计数会减1;
        还有 pShared2 = pShared1; 会对pShared2原来的引用计数减1.
        当引用计数为0的时候,shared_ptr就会去调用 对象的析构函数了。
       
        下面是weak_ptr对引用计数的影响,也只有两种情况,引用计数不变,或者引用计数加1.
        (1).引用计数不变的情况
            shared_ptr转换成 weak_ptr 时,引用计数不变。
            weak_ptr的赋值操作符,进行的是浅拷贝(也就是pShared1和pShared2的_Ptr和_Rep都指向的是同一个地址),并且引用计数的值不变。
            当然有其他的变化,这里忽略不记。
            ex:
            std::tr1::weak_ptr<A> pWeak;
            pWeak = pShared1;
           
        (2).引用计数加1
            weak_ptr转换为shared_ptr时,引用计数加1.
            ex:
            pShared2 = pWeak.lock();//这里会导致引用计数加1.
           
       
        weak_ptr使用时要注意的情况:
        weak_ptr对_Ptr的生命周期没有任何作用,只起到引用计数的增加或者不变。所以就很有可能我们在使用一个_Ptr已经释放了的weak_ptr.
        所以在使用的时候,我们应该使用下面的方法进行判断:
        方法1.(这种方法能增加_Ptr的生命周期)
        shared_ptr<A> t = pWeak.lock();
        if( t)....
        方法2.
        if(! pWeak.expired()) ...
       
        在weak_ptr离开作用域,去调用析构函数的时候,对引用计数没有影响,也就是引用计数不变。
       
       
     什么时候需要对 shared_ptr 加锁:
     唯一需要对shared_ptr加锁的情况是:对shared_ptr一个线程在读,另一个线程在写的情况。就是读写同时发生。
     ex:
     shared_ptr<A> s1;
    
     shared_ptr<A> s2(new A);
    
     shared_ptr<A> s3(new A);
     这里可能出现的问题是: s1=s2; 这个语句在线程1里面发生, s2=s3 在线程2里面发生。虽然 _Ptr 和引用计数的操作都是原子的,但是
     由于_Ptr和引用计数是两个原子操作,所以就可能引起问题。所以需要对s2的读写加锁。详解可以参见:http://blog.csdn.net/wwl33695/article/details/8561911
       
       
       
       
       
       
        http://blog.csdn.net/wwl33695/article/details/8561911
        http://blog.csdn.net/chdhust/article/details/9039367
        http://blog.csdn.net/pamxy/article/details/9096577
       
shared必须注意的问题
虽然boost.shared_ptr是个非常好的东西,使用它可以使得c++程序不需要考虑内存释放的问题,但是还是有很多必须注意的地方。下面罗列了一些本人在实际工作中经常碰到的使用shared_ptr出问题的几种情况。

1. shared_ptr多次引用同一数据,如下:

{

int* pInt = new int[100];

boost::shared_ptr<int> sp1(pInt);

// 一些其它代码之后…

boost::shared_ptr<int> sp2(pInt);

}

这种情况在实际中是很容易发生的,结果也是非常致命的,它会导致两次释放同一块内存,而破坏堆。

2. 使用shared_ptr包装this指针带来的问题,如下:

class tester

{

public:

       tester()

       ~tester()

       {

              std::cout << "析构函数被调用!\n";

       }

 

public:

       boost::shared_ptr<tester> sget()

       {

              return boost::shared_ptr<tester>(this);

       }

};

int main()

{

   tester t;

   boost::shared_ptr<tester> sp =  t.sget(); // …

return 0;

}

也将导致两次释放t对象破坏堆栈,一次是出栈时析构,一次就是shared_ptr的临时对象析构。若有这种需要,可以使用下面代码。

class tester : public boost::enable_shared_from_this<tester>

{

public:

       tester()

       ~tester()

       {

              std::cout << "析构函数被调用!\n";

       }

 

public:

       boost::shared_ptr<tester> sget()

       {

              return shared_from_this();

       }

};

int main()

{

   boost::shared_ptr<tester> sp(new tester);

 // 正确使用sp 指针。

 sp->sget();

 return 0;

}

 

3. shared_ptr循环引用导致内存泄露,代码如下:

class parent;

class child;

typedef boost::shared_ptr<parent> parent_ptr;

typedef boost::shared_ptr<child> child_ptr;

class parent

{

public:

       ~parent() {

              std::cout <<"父类析构函数被调用.\n";

       }

 

public:

       child_ptr children;

};

 

class child

{

public:

       ~child() {

              std::cout <<"子类析构函数被调用.\n";

       }

 

public:

       parent_ptr parent;

};

 

int main()

{

     parent_ptr father(new parent());

       child_ptr son(new child);

       // 父子互相引用。

       father->children = son;

       son->parent = father;

    return 0;

}

       如上代码,将在程序退出前,father的引用计数为2,son的计数也为2,退出时,shared_ptr所作操作就是简单的将计数减1,如果为0则释放,显然,这个情况下,引用计数不为0,于是造成father和son所指向的内存得不到释放,导致内存泄露。

 

 

 

make_shared:

因为shared_ptr的构造还需要new调用,这导致了代码中的某种不对称性。
想要在程序中消除new,我们可以使用工厂函数 make_shared
make_shared()函数比直接创建shared_ptr对象的方式快且高效,因为它内部仅分配了一次内存,消除了shared_ptr构造时的开销。

ex:
shared_ptr<string> sp = make_shared<string>("make shared"); 


将容器作为shared_ptr的管理对象,如share_ptr<List<T> >,使容器可以被安全地共享,用法与普通shared_ptr没有区别。

将shared_ptr作为容器的元素,如vector<shared_ptr<T> >,因为shared_ptr支持拷贝语义和比较操作,符合标准容器对元素的要求,
所以可以实现在容器中安全地容纳元素的指针而不是拷贝。

标准容器不能容纳auto_ptr和scoped_ptr,但可以容纳原始指针.

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值