C++智能指针

目录

一、普通指针

存在的缺陷:

二、智能指针

1.带引用计数的智能指针

1.1shared_ptr

1.2 weak_ptr

1.3小结  

2.不带引用计数的智能指针

2.1 auto_ptr

示例:

2.2 scoped_ptr

2.3unique_ptr

2.4 小结: 


一、普通指针

int *ptr;

存在的缺陷:

1.需要手动对指向的内存进行资源释放,否则会出现内存泄漏;

2.多个指针指向相同的内存地址,有一个指针对资源释放后,若没有对其他指针进行置空,那么容易产生访问内存异常不可控的错误;

二、智能指针

1.带引用计数的智能指针

        带引用计数的智能指针则可以同时多个指向同一资源。

刚开始引用计数为3(有三个指针指向new对象),当ptr1不指向new对象时,引用计数减一为2,当全部指针不指向对象new时,将对象new释放掉。 

带引用计数的智能指针包括 shared_ptr 和 weak_ptr。

1.1shared_ptr

shared_ptr 一般称为强智能指针,一个 shared_ptr 对资源进行引用时,资源的引用计数会增加一,通常用于管理对象的生命周期。只要有一个指向对象的 shared_ptr 存在,该对象就不会析构。上图中引用计数的工作过程就使用了 shared_ptr。

1.2 weak_ptr

weak_ptr 一般被称为弱智能指针,其对资源的引用不会引起资源的引用计数的变化,通常作为观察者,用于判断资源是否存在,并根据不同情况做出相应的操作。

比如使用 weak_ptr 对资源进行弱引用,当调用 weak_ptr 的 lock 方法时,若返回 ptr,则说明资源已经不存在,放弃对资源继续操作。否则,将返回一个 shared_ptr 对象,可以继续操作资源。

另外,一旦最后一个指向对象的 shared_ptr 被销毁,对象就会被释放。即使有 weak_ptr 指向对象,对象也还是会被释放。

1.3小结  

  • shared_ptr 会增加资源的引用计数,常用于管理对象的生命周期。
  • weak_ptr 不会增加资源的引用计数,常作为观察者用来判断对象是否存活。

2.不带引用计数的智能指针

不带引用计数的智能指针包括 auto_ptr 、scoped_ptr和unique_ptr。

2.1 auto_ptr

auto_ptr 模板定义了类似指针的对象,将 new 获得的地址赋给该对象。当 auto_ptr 对象过期时,析构函数将使用 delete 来释放内存。如果将 new 返回的地址赋值给 auto_ptr 对象,无须记住还需要释放这些内存。在 auto_ptr 对象过期时,内存将自动被释放。

template<class _Ty>class auto_ptr
{ 
    public: 
        typedef _Ty element_type;
    explicit auto_ptr(_Ty * _Ptr=ptr) noexcept : _Myptr(_Ptr)//初始化列表 
    { 
        //构造函数 
    }
    auto_ptr(auto_ptr& _Right) noexcept : _Myptr(_Right.release) 
    { 
        //拷贝构造函数,会调用release函数 
    } 
    _Ty * release noexcept { 
        /*使用拷贝构造时,最后一个auto_ptr持有资源, 其余被置为ptr*/  
        _Ty * _Tmp = _Myptr; 
        _Myptr = ptr; 
        return (_Tmp); 
    }
    private: 
        _Ty * _Myptr;//指向资源 
};

       使用拷贝构造时,如果只有最后一个 auto_ptr 持有资源,其余 auto_ptr 持有的资源会被置为 ptr。因此需要注意,不能在容器中使用 auto_ptr,当容器发生拷贝时,原容器中 auto_ptr 持有的资源会置 ptr

        当试图调用 auto_ptr 的拷贝构造函数时,在初始化列表中调用了 release 函数,release 函数用一个 _Tmp 指针保存资源并返回用于初始化当前的 auto_ptr 的类成员 _Myptr,而 _Right 对应的 _Myptr 被置为 ptr

只能对 new 分配的内存使用 auto_ptr 对象,不能对由 new() 分配的或通过声明变量分配的内存使用它。

auto_ptr <double> pd;
double *p_reg = new double;
pd = p_reg; // 不允许
pd = auto_ptr <double> (p_reg); //允许
auto_ptr <double> panto =p_reg; //不允许
auto_ptr <double> pauto (p_reg); //允许

示例:

#include<iostream>
#include<memory>
using namespace std;
int main()
{ 
    //定义auto_ptr指针ptr 
    std::auto_ptr<int> ptr(new int(6));
     //拷贝构造ptr定义ptr1 
    std::auto_ptr<int> ptr1(ptr); 
   //此时ptr已经为空指针,对空指针ptr赋值会产生不可预料的错误
    *ptr=8;
    return 0;
}  

        开始时 ptr 指向new出来的数字 6的地址,当用 ptr1 拷贝构造 ptr 时,ptr1 指向6的地址,而 ptr 则指向 NULL。下一行程序中如果对空指针 ptr 赋值 8,将会产生不可预料的错误。

2.2 scoped_ptr

与auto_ptr 不同,scoped_ptr私有化了拷贝构造函数和赋值函数,因此,资源的所有权无法进行转移,也无法在容器中使用,保证了资源的所有权。

template<class T> class scoped_ptr
{
    private: //私有化
        T * px;  
        scoped_ptr(scoped_ptr const &);//拷贝构造函数 
        scoped_ptr & operator=(scoped_ptr const &);//赋值构造函数
    public: 
        typedef T element_type; 
        explicit scoped_ptr( T * p = ptr ): px( p ) { } 
        ~scoped_ptr //析构函数
};

scoped_ptr 将拷贝构造函数和赋值构造函数定义为private权限 来阻止浅拷贝的发生。

scoped_ptr<int> sp1(new int(6));//初始化sp1指针,正确
scoped_ptr<int> sp2(sp1);//错误,无法拷贝构造,因为 scoped_ptr 私有化了拷贝构造函数,无法显式调用

scoped_ptr<int> sp3(new int(5));//初始化sp3指针 
sp1=sp3;//错误,无法赋值,因为 scoped_ptr 私有化了赋值构造函数

auto_ptr 是通过将除最后一个以外的其它 auto_ptr 置 ptr 来避免浅拷贝的发生,它的资源所有权是可以转移的。而 scoped_ptr 是直接禁止了拷贝与赋值,资源所有权无法转移。

2.3unique_ptr

unique_ptr 删除了拷贝构造函数和赋值函数,因此不支持普通的拷贝或赋值操作。

template<class _Ty,class _Dx>class unique_ptr: public _Unique_ptr_base<_Ty, _Dx>
{ 
    public: 
        typedef _Unique_ptr_base<_Ty, _Dx> _Mybase; 
        typedef typename _Mybase::pointer pointer; 
        typedef _Ty element_type; 
        typedef _Dx deleter_type;
        unique_ptr(unique_ptr&& _Right) noexcept : _Mybase(_Right.release, _STD forward<_Dx>(_Right.get_deleter)) 
         { 
             // 右值引用的拷贝构造函数 
         }
         unique_ptr& operator=(unique_ptr&& _Right) noexcept 
         { 
             //提供了右值引用的operator=赋值构造函数 
             if (this != _STD addressof(_Right)) 
             {  
                 reset(_Right.release); 
                 this->get_deleter = _STD forward<_Dx> (_Right.get_deleter); 
              } 
              return (*this);
          } 
          /* 删除了unique_ptr的拷贝构造和赋值函数,拒绝浅拷贝 */ 
          unique_ptr(const unique_ptr&) = delete; 
          unique_ptr& operator=(const unique_ptr&) = delete; 
};

unique_ptr 和 scoped_ptr 一样禁止了拷贝构造和赋值构造,引入了带右值引用的拷贝构造和赋值。可以把 unique_ptr 作为函数的返回值。

unique_ptr<int> p1(new int(6));//正确写法
unique_ptr<int> p2(p1); //这么写是错误的: unique_ptr不支持拷贝

unique_ptr<int> p3;
p3=p2;//这么写是错误的:unique_ptr不支持赋值

2.4 小结: 

不带引用计数的智能指针有:auto_ptr 、scoped_ptr、unique_ptr

  • 相同点:最终只有一个智能指针持有资源。
  • 不同点:
      • auto_ptr 进行拷贝构造时,会将之前的 auto_ptr 的ptr 置空;
      • scoped_ptr 通过私有化了拷贝构造和赋值函数杜绝浅拷贝;
      • unique_ptr 通过删除了拷贝构造和赋值函数函数杜绝浅拷贝,但引入了带右值引用的拷贝构造和赋值函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值