智能指针(auto_ptr 和 shared_ptr)

/* 以下内容摘自 http://zhoumf1214.blog.163.com/blog/static/524194020104203466852/ */
   STL中的auto_ptr指针是为了解决内存泄漏问题而设计的。它严格限制了指针拥有对指向对象的所有权。auto_ptr指针和普通指针的差别在于对指向对象所有权的处理不同。 auto_ptr指针是“传递”所有权,而普通指针是“共享”所有权。看下面例子:
std::auto_ptr<int> p1(new int(24));
std::auto_ptr<int> p2;
int *q1 = new int(12);
int *q2;
p2 = p1;
q2 = q1;
    经过两次赋值后,对于auto_ptr,p1为NULL,*p2为24;对于普通指针,*p1, *p2均为12。第一次赋值,p1把指向对象的所有权传递给了p2, p1不再拥有指向对象的所有权。而第二次赋值,q2和q1共享了对同一对象的所有权。因此,对于auto_ptr,一个对象只可能被一个智能指针指向,这样可有效避免内存泄漏问题。但是同时会引起新问题。看下面例子:
template<class T>
void BadPrint(std::auto_ptr<T> p)
{
    if (p.get() == NULL)
    {
         std::cout<<NULL;
    }
    else
    {
         std::cout<<*p;
    }
}
    然后我如下使用BadPrint函数:
std::auto_ptr<int> q(new int(18));
BadPrint(q);
*q = 12;
     该程序并未像我们所期望的一样:*q的值为12,而是会出现runtime error,why?因为我们把q作为函数参数传给了BadPrint,因此传递完成后q不再拥有对指向对象的所有权,而函数内部的局部变量p会接管q所指向对象的所有权,当函数执行完毕退出时,p的生命期截止同时delete所指向对象。因此q实际变为NULL,所以出错。如何避免出错?使用auto_ptr的引用?即 void BadPrint(std::auto_ptr<T> &p)。这是相当糟糕的一种做法。对智能指针使用引用混淆了所有权的问题。它导致所有权有可能被传递了,也可能没有被传递。无论如何应当避免对auto_ptr使用引用。
     可见智能指针并非对任何情况都智能。使用auto_ptr要知道:
1. 智能指针不能共享指向对象的所有权
2. 智能指针不能指向数组。因为其实现中调用的是delete而非delete[]
3. 智能指针不是万能的
4. 智能指针不能作为容器类的元素。例如:
template<class T>
void container::insert(const T &value)
{
   ..........
   X = value;
   ..........
}
事实上在stl中,所有的container要内部拷贝所传参数的值时都是传的const类型的值。因此无法用auto_ptr传进去。
 
其实现代码如下,注释详解了其功能用法:

/*
   auto_ptr模板类
            唯一成员数据是一个模板类型指针
 
            作用:动态分配对象以及当对象不再需要时自动执行清理

                 int* p = new int(0);
                 auto_ptr<int> ap(p);
                 从此不必关心应该何时释放p,也不用担心发生异常会有内存泄漏
            方式:
                  1.auto_ptr的做法是“所有权转移”,即拷贝或赋值的源对象将失去对“裸”指针的所有权,所以,与一般拷贝构造函数,赋值函数不同
                  2.拷贝或赋值的目标对象将先释放其原来所拥有的对象
            注意:
                 1.因为auto_ptr析构的时候肯定会删除他所拥有的那个对象,两个auto_ptr不能同时拥有同一个对象
                 2.auto_ptr的析构函数中删除指针用的是delete,而不是delete [],所以不应该用auto_ptr来管理一个数组指针
                 3.构造函数的explicit关键词有效阻止从一个“裸”指针隐式转换成auto_ptr类型
*/

template<typename _Tp> class auto_ptr
{
    private:
        _Tp* _M_ptr;
   
    public:
        typedef _Tp element_type;

  
    //构造函数
    //explicit表示该构造函数禁止隐式类型转换,即传入的参数必须是_Tp*类型
    explicit auto_ptr(element_type* __p = 0) throw() : _M_ptr(__p) {}

    //同类型拷贝构造函数,参数是另一个auto_ptr,但该auto_ptr要释放自己包含的指针
    //这是一个move(因此:千万不要使用类型为auto_ptr的容器)
    auto_ptr(auto_ptr& __a) throw() : _M_ptr(__a.release()) {}

    //不同类型的拷贝构造函数
    //也是move
    template<typename _Tpl>
    auto_ptr(auto_ptr<_Tpl>& __a) throw() : _M_ptr(__a.release()) {}

    //同类型auto_ptr赋值操作符,__a释放自己包含的指针
    //本模板类用reset更新自己的指针为__a指针
    auto_ptr& operator=(auto_ptr& __a) throw()
    {
        reset(__a.release());
        return *this;
    }


    //不同类型的auto_ptr赋值操作符
    template<typename _Tpl>
    auto_ptr& operator=(auto_ptr<_Tpl>& __a) throw()
    {
        reset(__a.release());
        return *this;
    }

    //析构函数
    //仅支持delete,不支持delete[]
    ~auto_ptr() { delete _M_ptr; }

    //重载dereference操作符
    element_type& operator* const throw()
    {
        _GLIBCXX_DEBUG_ASSERT(_M_ptr != 0);
        return *_M_ptr;
    }

    //重载->操作符
    element_type* operator->() cosnt throw()
    {
        _GLIBCXX_DEBUG_ASSERT(_M_ptr != 0);
        return _M_ptr;
    }

    //获取成员指针函数
    element_type* get() const throw() {  return _M_ptr; }
   
    //获取成员指针函数,同时将成员指针清零
    element_type* release() throw()
    {
         element_type* __tmp = _M_ptr;
         _M_ptr = 0;
         return _tmp;
    }

    //释放成员指针所指对象,同时给成员指针赋新值
    void reset(element_type* __p = 0) throw()
    {
         if(__p != _M_ptr)
         {
              delete _M_ptr;
              _M_ptr = __p;
         }
    }

    //从auto_ptr_ref构造auto_ptr的构造函数
    auto_ptr(auto_ptr_ref<element_type> __ref) throw() : _M_ptr(__ref._M_ptr) {  }

    //从auto_ptr_ref的赋值操作符
    //因为这里要释放内存,所以要进行self-assignment检查
    auto_ptr& operator=(auto_ptr_ref<element_type> __ref) throw()
    {
         if(__ref._M_ptr != this.get())
         {
              delete _M_ptr;
              _M_ptr = __ref._M_ptr;
         }
         return *this.
    }

    //本auto_ptr到其他任意类型auto_ptr_ref的conversation操作符重载
    template<typename _Tp1>
    operator auto_ptr_ref<_Tp1>() throw()
    {
         return auto_ptr_ref<_Tp1>(this->release());
    }
   
};


//模拟auto_ptr的reference类型
//可被一个传回auto_ptr值的函数赋值
template<typename _Tp1> struct auto_ptr_ref
{
    _Tp1* _M_ptr;
    explicit auto_ptr_ref(_Tpl* __p) : _M_ptr(__p) {  }
};

 

 

 

 

 

 

 

 

/* 以下内容摘自http://www.cppblog.com/expter/archive/2009/03/29/78270.aspx */

 

都是这周看书以及代码的总结:
  
    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);


要了解更多关于auto_ptr的信息,可以查看more effective c++ 的p158页条款28
要了解shared_ptr 类模板信息,可以查看boost 1.37.0中文文档,而且支持数组的shared_array 类模板

你可能感兴趣的:(exception,vector,null,delete,文档,reference)