智能指针(auto_ptr 和 shared_ptr)

   Stl  中 auto_ptr只是众多可能的智能指针之一,auto_ptr所做的事情,就是动态分配对象以及当对象不再需要时自动执行清理。
    这里是一个简单的代码示例,如果没有auto_ptr,
    

 1 void  ProcessAdoption(istream  & data)
 2 {
 3 
 4    while (data)                            // 如果还有数据
 5    {
 6        ALA   *pa = readALAData(data);      // 取出下一个数据
 7        pa->DealProcessAdoption(data);        // 处理
 8 
 9        delete pa;                          // 释放资源
10    }

11    return;
12}


     如果在DealProcessAdoption有一个exception,会发生什么事情,因为ProcessAdoption不能捕获他,所以这段代码很危险,所以DealProcessAdoption后面的代码可能会跳过,造成内存泄露。
如果利用try catch去捕获他,会搞得代码很乱,又缺少美观性。

所以Stl提供了一个智能指针来解决这个问题,我们可以先模拟实现一个智能指针的类实现。

 

 1 //  关于一个智能指针的定义
 2 template < typename Type >
 3 class  auto_ptr
 4 {
 5public:
 6    auto_ptr(T *=NULL) :Ptr(p)
 7    {     }
 8    ~auto_ptr()
 9    {
10        delete Ptr;
11    }

12private:
13    Type *Ptr;
14}
;
15
16
17 void  ProcessAdoption(istream  & data)
18 {
19
20    while (data)                            // 如果还有数据
21    {
22        auto_ptr<ALA> pa(readALADara(data));
23        pa->DealProcessAdoption(data);
24    }

25    return;
26}


这个版本和原先版本的差异只有二处,
第一pa是一智能指针的对象,不是ALA*
第二不用自己去释放delete

然后我看到Effective STL的条款
8:永不建立auto_ptr的容器
关于此可以看的Effective STL的条款8

因为auto_ptr并不是完美无缺的,它的确很方便,但也有缺陷,在使用时要注意避免。首先,不要将auto_ptr对象作为STL容器的元素。C++标准明确禁止这样做,否则可能会碰到不可预见的结果

auto_ptr的另一个缺陷是将数组作为auto_ptr的参数: auto_ptr<char>  pstr (new char[12] ); //数组;为定义
然后释放资源的时候不知道到底是利用delete pstr,还是 delete[] pstr;

然后收集了关于auto_ptr的几种注意事项:
1、auto_ptr不能共享所有权。
2、auto_ptr不能指向数组
3、auto_ptr不能作为容器的成员。
4、不能通过赋值操作来初始化auto_ptr
std::auto_ptr<int> p(new int(42));     //OK
std::auto_ptr<int> p = new int(42);    //ERROR
这是因为auto_ptr 的构造函数被定义为了explicit
5、不要把auto_ptr放入容器

然后笔者从而推荐的是boost的shared_ptr,然后看完shared_ptr关于智能指针的介绍与例子。
5种针对auto_ptr不足的指针如下:需要详细了解可以去查看相当文档,与测试新代码。

scoped_ptr<boost/scoped_ptr.hpp>简单的单一对象的唯一所有权。不可拷贝。
scoped_array<boost/scoped_array.hpp>简单的数组的唯一所有权。不可拷贝。
shared_ptr<boost/shared_ptr.hpp>在多个指针间共享的对象所有权。
shared_array<boost/shared_array.hpp>在多个指针间共享的数组所有权。
weak_ptr<boost/weak_ptr.hpp>一个属于 shared_ptr 的对象的无所有权的观察者。
intrusive_ptr<boost/intrusive_ptr.hpp>带有一个侵入式引用计数的对象的共享所有权。

1. shared_ptr是Boost库所提供的一个智能指针的实现,shared_ptr就是为了解决auto_ptr在对象所有权上的局限性(auto_ptr是独占的),在使用引用计数的机制上提供了可以共享所有权的智能指针.
2. shared_ptr比auto_ptr更安全
3. shared_ptr是可以拷贝和赋值的,拷贝行为也是等价的,并且可以被比较,这意味这它可被放入标准库的一般容器(vector,list)和关联容器中(map)。

关于shared_ptr的使用其实和auto_ptr差不多,只是实现上有差别,关于shared_ptr的定义就不贴代码了,以为内开源,可以网上找
1、shared_ptr<T> p(new Y);

 

 

shared_ptr是boost.smart_ptr库中最具价值、最有份量的组成部分。它与scoped_ptr一样包装了new操作符在堆上分配动态对象,但它实现的是引用计数(reference-count)型智能指针,可以被自由地拷贝和赋值,在任意的地方共享它。当引用计数值为0,也即没有代码使用它时它会自动删除被分配资源的对象。

shared_ptr可以安全地放到标准容器中,并弥补了auto_ptr因为转移语义而不能把指针STL容器元素的缺陷。C++历史上出现过很多引用计数型的智能指针,但没有一个比得上boost::shared_ptr的,过去,现在,将来它都是最好的。

类摘要:

  1. template<class T> class shared_ptr  
  2. {  
  3. private:  
  4.   
  5.     // Borland 5.5.1 specific workaround  
  6.     typedef shared_ptr<T> this_type;  
  7.   
  8. public:  
  9.   
  10.     typedef T element_type;  
  11.     typedef T value_type;  
  12.     typedef T * pointer;  
  13.     typedef typename boost::detail::shared_ptr_traits<T>::reference reference;  
  14.     shared_ptr();  
  15.     template<class Y>  
  16.     explicit shared_ptr( Y * p );  
  17.     template<class Y, class D> shared_ptr(Y * p, D d);  
  18.     template<class Y, class D, class A> shared_ptr( Y * p, D d, A a );  
  19.     template<class Y>  
  20.     explicit shared_ptr(weak_ptr<Y> const & r);  
  21.     template<class Y>  
  22.     shared_ptr( weak_ptr<Y> const & r, boost::detail::sp_nothrow_tag );  
  23.     template<class Y>  
  24.     shared_ptr( shared_ptr<Y> const & r, typename boost::detail::sp_enable_if_convertible<Y,T>::type = boost::detail::sp_empty() );  
  25.     shared_ptr( shared_ptr<Y> const & r );  
  26.     templateclass Y >  
  27.     shared_ptr( shared_ptr<Y> const & r, T * p );  
  28.     template<class Y>  
  29.     shared_ptr(shared_ptr<Y> const & r, boost::detail::static_cast_tag);  
  30.     template<class Y>  
  31.     shared_ptr(shared_ptr<Y> const & r, boost::detail::const_cast_tag);  
  32.     template<class Y>  
  33.     shared_ptr(shared_ptr<Y> const & r, boost::detail::dynamic_cast_tag);  
  34.     template<class Y>  
  35.     shared_ptr(shared_ptr<Y> const & r, boost::detail::polymorphic_cast_tag);  
  36.     template<class Y>  
  37.     explicit shared_ptr(std::auto_ptr<Y> & r);  
  38.     template<class Ap>  
  39.     explicit shared_ptr( Ap r, typename boost::detail::sp_enable_if_auto_ptr<Ap, int>::type = 0 );  
  40.     shared_ptr & operator=( shared_ptr const & r ) // never throws  
  41.     template<class Y>  
  42.     shared_ptr & operator=(shared_ptr<Y> const & r) //never throw  
  43.     template<class Y>  
  44.     shared_ptr & operator=( std::auto_ptr<Y> & r );  
  45.     template<class Ap>  
  46.     typename boost::detail::sp_enable_if_auto_ptr< Ap, shared_ptr & >::type operator=( Ap r );  
  47.     shared_ptr( shared_ptr && r );  
  48.     template<class Y>  
  49.     shared_ptr( shared_ptr<Y> && r, typename boost::detail::sp_enable_if_convertible<Y,T>::type = boost::detail::sp_empty() )  
  50.     shared_ptr( shared_ptr<Y> && r );  
  51.     shared_ptr & operator=( shared_ptr && r ); // never throws  
  52.      template<class Y>  
  53.     shared_ptr & operator=( shared_ptr<Y> && r ) // never throws  
  54.     void reset() ;// never throws in 1.30+  
  55.     template<class Y> void reset(Y * p) // Y must be complete  
  56.     template<class Y, class D> void reset( Y * p, D d );  
  57.     template<class Y, class D, class A> void reset( Y * p, D d, A a );  
  58.     template<class Y> void reset( shared_ptr<Y> const & r, T * p );  
  59.     reference operator* () const;// never throws  
  60.     T * operator-> () const// never throws  
  61.     T * get() const;// never throws  
  62.     bool unique() const// never throws  
  63.     long use_count(); const // never throws  
  64.     void swap(shared_ptr<T> & other);// never throws  
  65.     template<class Y> bool _internal_less(shared_ptr<Y> const & rhs) const;  
  66.     void * _internal_get_deleter( boost::detail::sp_typeinfo const & ti );  
  67.     bool _internal_equiv( shared_ptr const & r ) const;  
  68. private:  
  69.   
  70.     template<class Y> friend class shared_ptr;  
  71.     template<class Y> friend class weak_ptr;  
  72.     T * px;                     // contained pointer  
  73.     boost::detail::shared_count pn;    // reference counter  
  74.   
  75. };  // shared_ptr  
template<class T> class shared_ptr
{
private:

    // Borland 5.5.1 specific workaround
    typedef shared_ptr<T> this_type;

public:

    typedef T element_type;
    typedef T value_type;
    typedef T * pointer;
    typedef typename boost::detail::shared_ptr_traits<T>::reference reference;
    shared_ptr();
    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 );
    template<class Y>
    explicit shared_ptr(weak_ptr<Y> const & r);
    template<class Y>
    shared_ptr( weak_ptr<Y> const & r, boost::detail::sp_nothrow_tag );
    template<class Y>
    shared_ptr( shared_ptr<Y> const & r, typename boost::detail::sp_enable_if_convertible<Y,T>::type = boost::detail::sp_empty() );
    shared_ptr( shared_ptr<Y> const & r );
    template< class Y >
    shared_ptr( shared_ptr<Y> const & r, T * p );
    template<class Y>
    shared_ptr(shared_ptr<Y> const & r, boost::detail::static_cast_tag);
    template<class Y>
    shared_ptr(shared_ptr<Y> const & r, boost::detail::const_cast_tag);
    template<class Y>
    shared_ptr(shared_ptr<Y> const & r, boost::detail::dynamic_cast_tag);
    template<class Y>
    shared_ptr(shared_ptr<Y> const & r, boost::detail::polymorphic_cast_tag);
    template<class Y>
    explicit shared_ptr(std::auto_ptr<Y> & r);
    template<class Ap>
    explicit shared_ptr( Ap r, typename boost::detail::sp_enable_if_auto_ptr<Ap, int>::type = 0 );
    shared_ptr & operator=( shared_ptr const & r ) // never throws
    template<class Y>
    shared_ptr & operator=(shared_ptr<Y> const & r) //never throw
    template<class Y>
    shared_ptr & operator=( std::auto_ptr<Y> & r );
    template<class Ap>
    typename boost::detail::sp_enable_if_auto_ptr< Ap, shared_ptr & >::type operator=( Ap r );
    shared_ptr( shared_ptr && r );
    template<class Y>
    shared_ptr( shared_ptr<Y> && r, typename boost::detail::sp_enable_if_convertible<Y,T>::type = boost::detail::sp_empty() )
    shared_ptr( shared_ptr<Y> && r );
    shared_ptr & operator=( shared_ptr && r ); // never throws
     template<class Y>
    shared_ptr & operator=( shared_ptr<Y> && r ) // never throws
    void reset() ;// never throws in 1.30+
    template<class Y> void reset(Y * p) // Y must be complete
    template<class Y, class D> void reset( Y * p, D d );
    template<class Y, class D, class A> void reset( Y * p, D d, A a );
    template<class Y> void reset( shared_ptr<Y> const & r, T * p );
    reference operator* () const;// never throws
    T * operator-> () const; // never throws
    T * get() const;// never throws
    bool unique() const; // never throws
    long use_count(); const // never throws
    void swap(shared_ptr<T> & other);// never throws
    template<class Y> bool _internal_less(shared_ptr<Y> const & rhs) const;
    void * _internal_get_deleter( boost::detail::sp_typeinfo const & ti );
    bool _internal_equiv( shared_ptr const & r ) const;
private:

    template<class Y> friend class shared_ptr;
    template<class Y> friend class weak_ptr;
    T * px;                     // contained pointer
    boost::detail::shared_count pn;    // reference counter

};  // shared_ptr


shared_ptr重载了*和->操作符以模仿原始指针的行为,同时提供隐式bool类型转换判断指针是否有效。get()可以得到原始指针,没有提供指针算术操作。它可以正常地拷贝,赋值,也可以进行shared_ptr之间的比较,是最智能的智能指针。

shared_ptr有多种形式的构造函数,应用于各种可能的情形:

1,无参的shared_ptr()创建一个持有空指针的shared_ptr

2,shared_ptr(Y *p)获得指向类型T的指针p的管理权,同时引用计数置为1。这个构造函数要求T类型必须能够转换成Y类型

3,shared_ptr(shared_ptr const &r)从另外一个shared_ptr获得指针的管理权,同时引用计数为1,结果是两个shared_ptr共享一个指针的管理权。

4,shared_ptr(std::auto<Y> &r)从一个auto_ptr获得指针的管理权,引用计数置为1,同时auto_ptr自动失去管理权。

5,operator=赋值操作符可以从另外一个shared_ptr或auto_ptr获得指针的管理权,其行为同构造函数

6,shared_ptr(Y *p,D d)行为类似shared_ptr(Y *p),但使用参数d指定了析构时的定制删除器,而不是简单的delete。

 

shared_ptr的reset()函数是将引用计数减1,停止对指针的共享,除非引用计数为0,否则不会发生删除操作。带参数的reset()则类似相同形式的构造函数,原指针引用计数减1的同时改为管理另外一个指针。

shared_ptr有两个函数来检查引用计数,unique()在shared_ptr是指针的唯一所有者时返回true(这时的行为类似auto_ptr或scoped_ptr),use_count()返回当前指针的引用计数。不过小心了,use_count()仅仅用于测试或者调试,它不提供高效的操作,而且有时可能是不可用的。但是unique()是可靠的,任何时候都可以用,而且比use_count() == 1速度更快。

shared_ptr支持比较运算,可以测试两个shared_ptr的相等或不相等,比较基于内部保存的指针,相当于a.get() == b.get() 。还可以使用operator<比较大小,同样基于内部保存的指针,但不提供除它以外的比较符,这使得shared_ptr可以被应用于标准关联容器(set和map):

typedef shared_ptr<string> sp_t;

map<sp_t,int > m; //标准映射容器

sp_t sp(new string("one"));

m[sp] = 111; 

在编写基于虚函数的多态代码时指针的类型转换很有用,比如把一个基类指针转换成一个继承类指针或者反过来(继承类指针转换成基类指针)。但对于shared_ptr不能使用诸如static_cast<T *>(p.get())的形式。这将导致转型后的指针无法再被shared_ptr正确管理。为了支持这种用法,shared_ptr提供了类似的转型函数static_pointer_cast<T>() ,const_pointer_cast<T>, dynamic_pointer_cast<T>类似,但它返回的是转型后的shared_ptr。

例如,下面的代码使用dynamic_pointer_cast把一个shared_ptr<std::exception>向下转型为一个shared_ptr<bad_exception>,然后又用static_pointer_cast重置转为shared_ptr<std::exception>;

shared_ptr<std::exception> sp1(new bad_exception("error"));

shared_ptr<bad_exception> sp2 = dynamic_pointer_cast<bad_exception>(sp1);

shared_ptr<std::exception> sp3 = static_pointer_cast<std::exception>(sp2);

assert(sp3 == sp1);

shared_ptr还支持流输出操作符operator<<,输出内部的指针值,方便调试。

 

使用示例:

  1. #include <iostream>   
  2. #include <boost/smart_ptr.hpp>   
  3. using namespace boost;  
  4. using namespace std;  
  5. class shared //define a custom class  
  6. {  
  7.  public:  
  8.  shared(shared_ptr<int> p):_p(p){}  
  9.  void print()  
  10.  {  
  11.   cout << "count: " << _p.use_count()  
  12.        << "  v= " << *_p << endl;  
  13.  }  
  14.  private:  
  15.  shared_ptr<int> _p;  
  16. };  
  17. void print_func(shared_ptr<int> p)  
  18. {  
  19.  cout << "count: " << p.use_count()   
  20.       << "  v= " << *p << endl;  
  21. }  
  22. int main()  
  23. {  
  24.  /* 
  25.  /*example1 
  26.  shared_ptr<int> sp(new int(10));//a smart_ptr pointed to int 
  27.  assert(sp.unique()); //shared_ptr is the only owner of pointer 
  28.   
  29.  shared_ptr<int> sp2 = sp; //the second shared_ptr,copy construct 
  30.  assert(sp == sp2 && sp.use_count() == 2);  
  31.  //Two shared_ptr equal, pointing in the same object, reference counting is 2 
  32.  *sp2 = 100; //now print *sp2 
  33.  assert(*sp == 100); 
  34.  sp.reset();// 
  35.  assert(!sp);//sp now is empty 
  36.  cout << "no problem." << endl; 
  37.      */  
  38.  /*example2*/  
  39.  shared_ptr<int> p(new int(100));  
  40.  shared s1(p),s2(p);//custom shared class object  
  41.    
  42.  s1.print(); //3   
  43.  s2.print();//3   
  44.    
  45.  *p = 20; //modify the value of the object which p point to  
  46.  print_func(p);//display use_count 4   
  47.  s1.print();//3   
  48.    
  49. }  
#include <iostream>
#include <boost/smart_ptr.hpp>
using namespace boost;
using namespace std;
class shared //define a custom class
{
 public:
 shared(shared_ptr<int> p):_p(p){}
 void print()
 {
  cout << "count: " << _p.use_count()
       << "  v= " << *_p << endl;
 }
 private:
 shared_ptr<int> _p;
};
void print_func(shared_ptr<int> p)
{
 cout << "count: " << p.use_count() 
      << "  v= " << *p << endl;
}
int main()
{
 /*
 /*example1
 shared_ptr<int> sp(new int(10));//a smart_ptr pointed to int
 assert(sp.unique()); //shared_ptr is the only owner of pointer
 
 shared_ptr<int> sp2 = sp; //the second shared_ptr,copy construct
 assert(sp == sp2 && sp.use_count() == 2); 
 //Two shared_ptr equal, pointing in the same object, reference counting is 2
 *sp2 = 100; //now print *sp2
 assert(*sp == 100);
 sp.reset();//
 assert(!sp);//sp now is empty
 cout << "no problem." << endl;
     */
 /*example2*/
 shared_ptr<int> p(new int(100));
 shared s1(p),s2(p);//custom shared class object
 
 s1.print(); //3
 s2.print();//3
 
 *p = 20; //modify the value of the object which p point to
 print_func(p);//display use_count 4
 s1.print();//3
 
}


 

运行结果:

count: 3  v= 100
count: 3  v= 100
count: 4  v= 20
count: 3  v= 20

注意,我们没有使用引用方式传递参数,而是直接拷贝,就像是使用一个原始指针,shared_ptr支持这样的用法。

在声明了share_ptr和两个shared类实例后,指针被它们所共享,因此引用计数为3。print_fun()函数内部拷贝了一个shared_ptr对象,因此引用计数再增加1,退出的时候拷贝自动析构,引用计数又恢复到了3

 

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

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

class implementation
{
public:
    ~implementation() { std::cout <<"destroying implementation\n"; }
    void do_something() { std::cout << "did something\n"; }
};

void test()
{
    boost::shared_ptr<implementation> sp1(new implementation());
    std::cout<<"The Sample now has "<<sp1.use_count()<<" references\n";

    boost::shared_ptr<implementation> sp2 = sp1;
    std::cout<<"The Sample now has "<<sp1.use_count()<<" references\n";
    
    sp1.reset();
    std::cout<<"After Reset sp1. The Sample now has "<<sp2.use_count()<<" references\n";

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

void main()
{
    test();
}

该程序的输出结果如下:

The Sample now has 1 references
The Sample now has 2 references
After Reset sp1. The Sample now has 1 references
destroying implementation
After Reset sp2.

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

boost::shared_ptr的内存管理机制:

boost::shared_ptr的管理机制其实并不复杂,就是对所管理的对象进行了引用计数,当新增一个boost::shared_ptr对该对象进行管理时,就将该对象的引用计数加一;减少一个boost::shared_ptr对该对象进行管理时,就将该对象的引用计数减一,如果该对象的引用计数为0的时候,说明没有任何指针对其管理,才调用delete释放其所占的内存。

上面的那个例子可以的图示如下:

  1. sp1对implementation对象进行管理,其引用计数为1
  2. 增加sp2对implementation对象进行管理,其引用计数增加为2
  3. sp1释放对implementation对象进行管理,其引用计数变为1
  4. sp2释放对implementation对象进行管理,其引用计数变为0,该对象被自动删除

boost::shared_ptr的特点:

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

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

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值