C++智能指针

一、认识智能指针

c++的内存管理是让很多人头疼的事,当我们写一个new语句时,有时候我们不能避免程序还未执行到delete时就跳转了或者在函数中没有执行到最后的delete语句就返回了。
如果我们不在每一个可能跳转或者返回的语句前释放资源,就会造成内存泄露。使用智能指针可以很大程度上的避免这个问题

二、智能指针的原理

把管理一份资源的责任托管给了一个对象
在对象构造时获取资源,最后在对象析构的时候释放资源。

三、几种智能指针

auto_ptr(C++98)

template<class T>
class AutoPtr
{
public:
  AutoPtr(T* ptr = NULL)
    : _ptr(ptr)
  {}
 
  ~AutoPtr()
  {
    if(_ptr)
    delete _ptr;
  }
 
 // 一旦发生拷贝,就将ap中资源转移到当前对象中,然后让ap与其所管理资源
 //断开联系,这样就解决了一块空间被多个对象使用而造成程序奔溃问题
 AutoPtr(AutoPtr<T>& ap)
  : _ptr(ap._ptr)
  {
    ap._ptr = NULL;
  }
 
 AutoPtr<T>& operator=(AutoPtr<T>& ap)
 {
   // 检测是否为自己给自己赋值
   if(this != &ap)
   {
    // 释放当前对象中资源
     if(_ptr)
       delete _ptr;
 
    // 转移ap中资源到当前对象中
     _ptr = ap._ptr;
     ap._ptr = NULL;

     return *this;
 }
 
   T& operator*() {return *_ptr;}
   T* operator->() { return _ptr;}
private:
   T* _ptr;
};
int main()
{
   AutoPtr<Date> ap(new Date);
 
 // 现在再从实现原理层来分析会发现,这里拷贝后把ap对象的指针赋空了
 //导致ap对象悬空,通过ap对象访问资源时就会出现问题。
   AutoPtr<Date> copy(ap);
   ap->_year = 2018;
   return 0; 
 }

unique_ptr(C++11)

unique_ptr的实现原理:简单粗暴的防拷贝

template<class T>
class UniquePtr
{
public:
  UniquePtr(T * ptr = nullptr) 
    : _ptr(ptr)
    {}
 ~UniquePtr() 
 {
   if(_ptr)
   delete _ptr;
 }
  T& operator*() {return *_ptr;}
  T* operator->() {return _ptr;}
 
private:
   // C++98防拷贝的方式:只声明不实现+声明成私有
   UniquePtr(UniquePtr<T> const &);
   UniquePtr & operator=(UniquePtr<T> const &);
 
   // C++11防拷贝的方式:delete
   UniquePtr(UniquePtr<T> const &) = delete;
   UniquePtr & operator=(UniquePtr<T> const &) = delete;
 
private:
   T * _ptr;
};

如果一个unique_ptr想赋值给另一个unique_ptr,可以使用move( ):
unique_ptr 不能直接赋值给另一个 unique_ptr,但可以通过 std::move 函数来实现所有权的转移。

	std::unique_ptr<int> p1(new int(42));
	std::unique_ptr<int> p2;
	
	// 将 p1 的所有权转移到 p2 中
	p2 = std::move(p1);

注意,在转移所有权之后,p1 不再拥有指向原对象的所有权,因此不能再次使用 p1

shared_ptr(C++11)

shared_ptr的原理:是通过引用计数的方式来实现多个shared_ptr对象之间共享资源。

  1. shared_ptr在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象共享。
  2. 在对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减一。
  3. 如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源。
  4. 如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了
#include <thread>
#include <mutex>
template <class T>
class SharedPtr
{
public:
    SharedPtr(T* ptr = nullptr)
      : _ptr(ptr)
      , _pRefCount(new int(1))
      , _pMutex(new mutex)
   {}
    ~SharedPtr() {Release();}
    SharedPtr(const SharedPtr<T>& sp)
      : _ptr(sp._ptr)
      , _pRefCount(sp._pRefCount)
      , _pMutex(sp._pMutex)
   {
     AddRefCount();
   }
   // sp1 = sp2
    SharedPtr<T>& operator=(const SharedPtr<T>& sp)
    {
       //if (this != &sp)
        if (_ptr != sp._ptr)
        {
			 // 释放管理的旧资源
			 Release();
			 // 共享管理新对象的资源,并增加引用计数
			 _ptr = sp._ptr;
			 _pRefCount = sp._pRefCount;
			 _pMutex = sp._pMutex;
			 
			 AddRefCount();
        }
         return *this;
     }
    T& operator*() {return *_ptr;}
    T* operator->() {return _ptr;}
    int UseCount() {return *_pRefCount;}
    T* Get() { return _ptr; }
    void AddRefCount()
    {
	 // 加锁或者使用加1的原子操作
	 _pMutex->lock();
	 ++(*_pRefCount);
	
	 _pMutex->unlock();
    }
private:
 void Release()
 {
	 bool deleteflag = false;
 
	 // 引用计数减1,如果减到0,则释放资源
	 _pMutex.lock();
	 if (--(*_pRefCount) == 0)
	 {
	 delete _ptr;
	 delete _pRefCount;
	 deleteflag = true;
	 }
	 _pMutex.unlock();
	 
	 if(deleteflag == true)
	 delete _pMutex;
 }
private:
	 int* _pRefCount; // 引用计数
	 T* _ptr; // 指向管理资源的指针 
	 mutex* _pMutex; // 互斥锁
};

std::shared_ptr的线程安全问题

  1. 智能指针对象中引用计数是多个智能指针对象共享的,C++11之后的标准操作引用计数是原子的,之前不是原子的,需要加锁。但是Boost库中的shared_ptr在03标准就实现了引用计数的原子操作。
  2. 智能指针管理的对象存放在堆上,两个线程同时去访问,会导致线程安全问题。

weak_ptr

shared_ptr可能会导致循环引用
这时需要使用weak_ptr,不增加引用计数。

四、补充

参考这里🔗

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

江南无故人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值