C++ 智能指针( C++11与boost库的智能指针及其使用)

1 智能指针的引入

有时候,我们new了,也delete了,但是还会出现问题。例如在new和delete之间调用了某个抛异常的函数,就有可能导致没有执行delete。

例如:fun2里面使用了new[],最后也使用了delete[],仍然会有内存泄露的问题。

void fun1()
{
      throw int(11);
}
void fun2()
{
      int* p = new int[10000];
      fun1();
      delete[] p;
}

int main()
{
      try
      {
           fun2();
      }
      catch (int& e)
      {
           cout << "捕获" << endl;
      }
      system("pause");
      return 0;
}

要解决上面的问题,有一种方法:就是如果发现delete之前发现某个函数抛出了异常,就在delete之前捕获这个异常,并且在catch语句里面进行资源的释放,并且可以再将这个异常重新抛出。

void fun2()
{
      int* p = new int[10000];
      try
      {
           fun1();
      }
      catch(int& e)
      {
           delete[] p;
           cout << "重新抛出" << endl;
           throw;
      }
      delete[] p;
}

但是这种方法写着比较繁琐。
4.智能指针就有上面的作用,能够自动的管理指针所指向的动态资源的释放。它不仅有着RAII的思想还能够像指针一样。(RAII:资源分配即初始化,即构造函数分配资源和初始化资源,在析构函数清理资源。像指针一样:能够解引用)。
5.智能指针实质上就是一个模板类,成员变量包含一个任意类型的指针,构造函数获得资源并初始化,析构函数清理资源。
注意:智能指针只能管理动态开辟的空间。

2 智能指针的发展史

当用一个智能指针拷贝构造另一个智能指针的时候,有可能会有浅拷贝的问题,这个空间会被释放多次,智能指针的发展就是围绕着指针拷贝问题而走。

2.1 auto_ptr

(1)C++98里面有一个智能指针auto_ptr,对于拷贝构造和赋值运算符重载,该智能指针采用管理权转移的方式(当一个指针拷贝构造另一个指针时,当前指针就将对空间的管理权交给拷贝的那个指针,当前指针就指向空);
(2)但是这种方式不符合指针的要求(可以允许多个指针指向同一块空间,将一个指针赋值给另一个指针的时候,就是需要让两个指针指向同一块空间,而auto_ptr只允许一块空间上只能有一个指针指向它),并且当管理权转移之后要想再访问之前的指针,就会出错,因为之前的指针已经为NULL,就会出现引用空指针的问题。

2.2 scoped_ptr/shared_ptr

因为auto_ptr有缺陷,但是C++标准里面从C++98到C++11之间没有出现新的智能指针能解决这个缺陷,所以在这段时间内,boost这个官方组织就增加了智能指针(scoped_ptr,shared_ptr,weak_ptr等)

(1)scoped_ptr采用防拷贝的方式(防拷贝就是不允许拷贝,拷贝就会出错;防拷贝的实现:将拷贝构造和赋值运算符重载只声明不实现,并且声明为私有);
(2)shared_ptr为共享指针,里面采用引用计数,当有shared_ptr指向同一块空间的时候就增加引用计数,当引用计数减为0的时候才释放该智能指针管理的那块空间。
(3)但是shared_ptr有一个缺点,就是会出现循环引用的问题(当一个shared_ptr(如sp1)管理的空间里面包含一个shared_ptr的指针(_next),另一个shared_ptr(如sp2)管理的空间里面也包含一个shared_ptr指针(_prev)时,当sp1->_next = sp2;sp2->_prev = sp1;此时就会使得sp1和sp2的引用计数都变为2,当出了这个作用域sp1和sp2的引用计数都会减为1,但是只有引用计数为0时才会释放管理的空间,就会使得sp1和sp2管理的空间没有释放。
(4)所以利用weak_ptr来解决循环引用的问题,weak_ptr叫弱指针,它主要是为了配合shared_ptr使用,用来解决循环引用的问题;

  • 将会出现循环引用问题的指针用weak_ptr保存着,weak_ptr并不拥有这块空间,所以weak_ptr里面不增加shared_ptr的引用计数,也不会释放这块空间。(注意weak_ptr里面也有自己的引用计数)

(5)boost库里面还包含scoped_array和shared_array(这个适用于delete[]的场景)

2.3 C++11(unique_ptr和shared_ptr)

(1)C++11借鉴了boost库里面的智能指针(C++对应的智能指针位于头文件<memory>里。

C++11里面的unique_ptr就是boost库里面的scoped_ptr(防拷贝);
C++11里面的shared_ptr就是boost里面的shared_ptr。

(2)C++11里面不包含类似于scoped_array和shared_array,而它采用定制删除器的方式管理空间的释放。

  • 定制删除器就是自己指定采用何种方式释放该空间(delete/free或其它)(例如new出来的数据,就必须用delete,而new[ ]就需要delete[ ]等)。注意:boost库里面也包含定制删除器。

区别:

unique_ptr不可复制 ,但支持转移

scoped_ptr 既不可复制也不可移动 。 这是当想出作用域时,确保指针被删除的首选。

3 auto_ptr

1.源代码分析:

template<class _Ty>
class auto_ptr
{  
public:
    typedef auto_ptr<_Ty> _Myt;
    typedef _Ty element_type;
    
    //构造函数
    explicit auto_ptr(_Ty *_Ptr = 0) _THROW0()
        : _Myptr(_Ptr)
    {}
    
    //拷贝构造函数,release返回保存_Right指向空间的临时变量,则_Myptr管理_Right管理的空间,并将_Right置为空
    auto_ptr(_Myt& _Right) _THROW0()
        : _Myptr(_Right.release())
    {}
    
    //赋值运算符重载,_Right.release()将_Right的管理权转移给_Myptr,将_Right置为空。
    //reset里面判断是不是自己给自己赋值,不是就释放_Myptr。
    _Myt& operator=(_Myt& _Right) _THROW0()
    {  
        reset(_Right.release());
        return (*this);
    }
    
    //析构函数释放_myptr
    ~auto_ptr() _NOEXCEPT
    {    
        delete _Myptr;
    }
    
    _Ty& operator*() const _THROW0()
    {  
#if _ITERATOR_DEBUG_LEVEL == 2
        if (_Myptr == 0)
            _DEBUG_ERROR("auto_ptr not dereferencable");
#endif 
        
        return (*get());
    }
    
    _Ty *operator->() const _THROW0()
    {    
#if _ITERATOR_DEBUG_LEVEL == 2
        if (_Myptr == 0)
            _DEBUG_ERROR("auto_ptr not dereferencable");
#endif
        return (get());
    }
    
    //get返回原生指针
    _Ty *get() const _THROW0()
    {     
        return (_Myptr);
    }
    
    //release用于管理权的转移,将_myptr保存在tmp里面,将_mytmp的指针置为空,再返回tmp
    _Ty *release() _THROW0()
    {     // return wrapped pointer and give up ownership
        _Ty *_Tmp = _Myptr;
        _Myptr = 0;
        return (_Tmp);
    }
    
    void reset(_Ty *_Ptr = 0)
    {   
        if (_Ptr != _Myptr)
            delete _Myptr;
        _Myptr = _Ptr;
    }
    
private:
    _Ty *_Myptr;     // the wrapped object pointer
};

2.使用:(必须包含头文件<memory>)
将动态开辟的指针交给一个智能指针。

void Test_auto_ptr()
{
      auto_ptr<int> ap1(new int(10));
      cout << *ap1 << endl;  //输出10

      auto_ptr<int> ap2(ap1);
      cout << *ap2 << endl;   //输出10
      cout << *ap1 << endl;   //此时ap1为NULL,就是解引用空指针,程序崩溃
}

 scoped_ptr

1.源代码:

template<class T> 
class scoped_ptr 
{
private:

    T * px;    //只含有一个成员变量T*的指针

     //将拷贝构造与赋值运算符重载声明为私有,且不实现
    scoped_ptr(scoped_ptr const &);
    scoped_ptr & operator=(scoped_ptr const &);

    typedef scoped_ptr<T> this_type;

    void operator==( scoped_ptr const& ) const;
    void operator!=( scoped_ptr const& ) const;

public:

    typedef T element_type;

     //构造函数
    explicit scoped_ptr( T * p = 0 )
          : px( p ) 
    {
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
        boost::sp_scalar_constructor_hook( px );
#endif
    }

#ifndef BOOST_NO_AUTO_PTR

    explicit scoped_ptr( std::auto_ptr<T> p ) BOOST_NOEXCEPT 
         : px( p.release() )
    {
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
        boost::sp_scalar_constructor_hook( px );
#endif
    }

#endif

     //析构函数
    ~scoped_ptr() 
    {
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
        boost::sp_scalar_destructor_hook( px );
#endif
        boost::checked_delete( px );
    }

    void reset(T * p = 0) 
    {
        BOOST_ASSERT( p == 0 || p != px ); 
        this_type(p).swap(*this);
    }

    T & operator*() const 
    {
        BOOST_ASSERT( px != 0 );
        return *px;
    }

    T * operator->() const 
    {
        BOOST_ASSERT( px != 0 );
        return px;
    }

     //获得原生指针
    T * get() const BOOST_NOEXCEPT
    {
        return px;
    }

#include <boost/smart_ptr/detail/operator_bool.hpp>

    void swap(scoped_ptr & b) BOOST_NOEXCEPT
    {
        T * tmp = b.px;
        b.px = px;
        px = tmp;
    }

2.使用:
当使用boost库的时候,必须先要从boost官方网站下载boost的源码,然后将从boost库下载的源代码所在目录包含至自己的项目,并且使用时要指定命名空间为boost。

#include<iostream>
#include<string>
#include<boost/scoped_ptr.hpp>
#include<boost/scoped_array.hpp>

int main()
{
      boost::scoped_ptr<int> sp1(new int(10));

      //boost::scoped_ptr<int> sp2(sp1);     //不能拷贝

      boost::scoped_array<std::string> sp2(new std::string[10]);

      return 0;
}

5 boost库shared_ptr的使用

void Test_boost_shared_ptr()
{
      boost::shared_ptr<int> sp1(new int(10));
      std::cout << *sp1 << std::endl;    //输出10

      boost::shared_ptr<int> sp2(sp1);
      std::cout << *sp2 << std::endl;    //输出10

      boost::shared_array<std::string> sp3(new std::string[10]);
      sp3[5] = "111";     //shared_array里面重载了[],所以可以采用下标的方式进行读写

      boost::shared_array<std::string> sp4(sp3);
      std::cout << sp4[5] << std::endl;   //输出111
}

6 C++11里shared_ptr的使用

1.基本使用与boost里shared_ptr的使用方式一致

void Test_Shared_ptr()
{
      std::shared_ptr<int> sp1(new int(20));
      std::shared_ptr<int> sp2(sp1);
      std::cout << *sp2 << std::endl;
}

2.定制删除器:
(1)首先自己定制删除器(例如我定制了一个delete和一个delete[ ])
自己需要编写相应的仿函数,在构造shared_ptr对象时需要传相应仿函数的对象。

template<class T>
struct Delete
{
      void operator()(T* ptr)
      {
           delete ptr;
      }
};

template<class T>
struct DeleteArray
{
      void operator()(T* ptr)
      {
           delete[] ptr;
      }
};

(2)使用:

void Test_Shared_ptr()
{
      DeleteArray<std::string> da;
      std::shared_ptr<std::string> sp3(new std::string[10],da); 
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值