0
点赞
收藏
分享

微信扫一扫

C++中的智能指针

秦瑟读书 2022-04-19 阅读 48
c++

1.RAlI与智能指针

1.1RALL

RAll (Resource Acquisition ls lnitialization) 是由c++之父Bjarne Stroustrup提出的,中文翻译为资源获取即初始化-即使用局部对象来管理资源的技术;这里的资源主要是指操作系统中有限的东西,如内存(heap)、网络套接字,互斥量,文件句柄等,局部对象是指存储在的对象,它的生命周期是由操作系统来管理的,无需人工介入。

资源的使用经历三个步骤

  • 获取资源(创建资源)
  • 使用资源
  • 销毁资源(析构对象)

RAII方案利用局部对象自动销毁的特点来控制资源的生命周期,从而实现资源的自动销毁,进而避免了程序员忘记主动销毁资源所引发得不必要的问题

为什么使用智能指针?

直接使用传统的指针管理内存会存在以下问题
在这里插入图片描述
总而言之,使用传统的指针在cpp中存在很大的内存泄露风险

c++中的智能指针
在这里插入图片描述

1.2auto_ptr的弃用原因

template<class _Ty>
class my_auto_ptr
{
private:
      bool _Owns;
      _Ty* _Ptr;
publicmy_auto_ptr(_Ty* p = NULL):_Owns(p != NULL)_Ptr(p){}
  
  ~my_auto_ptr()
  {
     if(_Owns){ delete _Ptr;}
     _Owns = false;
     _PTr = NULL;
  }
 
   _Ty* get() const 
   { return _Ptr; }

//以&返回是为得到_Ptr所指向的实体
   _Ty& operator* () const
    { return*(get(); }
  
   _Ty* operator->() const
    { return get(); }

  my_auto_ptr& operalor=(const my_auto_ptr& _Y)
  {
    if (this == &_Y) return *this;
    if (_Owns)
    {  delete _Ptr; }
    _Owns = _Y._Owns;
    _Ptr = _Y.release() ;
    return 0;
  }

};
void fun {
my_auto_ptr<Object>obj(new Object (10));

cout <<obj->Value() << endl ;
cout <<(*obj).Value()<< endl;
 
}

弃用的原因

  • 1.拷贝构造函数与赋值语句的意义不明确
    在这里插入图片描述
    如上例中使用浅拷贝(共享拥有权Owns)会致使同一空间被释放两次,引发程序崩溃;

如果不共享拥有权,改为使用release()函数来实现拥有权的转移.由此在执行赋值或拷贝构造后,原指针的_Owns被设为false,无法再通过原指针访问其对应资源,这有时并不是我们想要的结果.

  • 2.仍未解决传统数组中,对单个对象delete和对数组指针delete[]的冲突问题
int main()
{
 my_auto_ptr<int> pobja (new int[10]);//T
 my_auto_ptr<Object> pobjb(new Object[10]);//F,显然my_auto_ptr中的析构函数无法应对这种情况
 return 0;
}

auto_ptr因为无法解决上述问题,使得auto_ptr无法与STL库中的容器关联

auto_ptr最终于C++11标准中被弃用.于C++17标准中被移除

1.3unique_ptr(唯一型智能指针) 的使用

1.3.1对使用权问题的解决

为解决auto_ptr中使用权Owns的不明确问题,因而引入unique_ptr(唯一型智能指针),该指针相对于auto_ptr不具有bool _Owns,其对资源的使用权具有唯一性,unique_ptr唯一拥有其指向的资源空间(因此不需添加bool _Owns对其标致)

#include<memory> //智能指针存于此库中


template<class _Ty> 
class my_unique_ptr
{
private:
  _Ty*_Ptr;
 my_unique_ptr(const my_unique_ptr&) = delete; //禁用了拷贝构造函数

  my_unique_ptr& operator=(const my_unique_ptr&) = delete;//禁用了赋值语句重载

public:
  typedef _Ty*pointer;
  typedef _Ty element_type;
  
  my_unique_ptr(_Ty* p = NULL):_Ptr(p){}
  ~my_unique_ptr(){ delete _Ptr;}

  //移动构造
  my_unique_ptr(my_unique_ptr&&_Y)
  {
   Ptr = _Y._Ptr;
   _Y._Ptr = nullptr;
  }
   //移动赋值
 my_unique_ptr& operator=(my_unique_ptr&&_Y)
  {
   if(this != &_Y)
   {
    delete _ptr;
    Ptr = _Y._Ptr;
    Y.Ptr = NULL;
   }
   return *this;
  }

  
};

//F,unique_ptr禁用了拷贝构造函数
void fun(std::unique_ptr<Object> test)
{···}


int main()
{
 std::unique_ptr<int> pobja (new int[10]);//T
 
  std::unique_ptr<Object> pobjb(pobja);//F,unique_ptr禁用了拷贝构造函数

  std::unique_ptr<Object> pobjc;
  pobjc = pobja;//F,unique_ptr禁用了=运算符重载函数
  
  std::unique_ptr<Object> pt(std::move(pobja))//使用拷贝移动构造实现构造-拷贝移动构造,实现资源的转移
  
  std::unique_ptr<Object> pt2;
  pt2 = std::move(pobja);//T,底层上将pobja强转为std::unique_ptr<Object>&& 类型

  
 return 0;
}

1.3.2对单个对象与数组指针delete冲突问题的解决

unique_ptr通过辅助类型Deletor的帮助达到对该问题的解决,见下例

//自建辅助类型Deletor对普通类型的接受方案
template<class _Ty>
class MyDeletor
{
public:
  myDeletor(){};
  void operator()(_Ty* ptr) const
  {
    if (ptr != nullptr)
    {
     delete ptr;
    }
  }
};

//对Deletor部分特化,使其能够接收数组类型
template<class _Ty>
class MyDeletor<_Ty[]>
{
public:
  myDeletor() = default ; //c11标准。意为添加一个默认函数,等效于 myDeletor(){};
  void operator()(_Ty* ptr) const
  {
    if (ptr != nullptr)
    {
     delete[]ptr;
    }
  }
};


int main()
{
  MyDeletor<Object> op;
  MyDeletor<Object[]> arop;
//通过自建的辅助类型Deletor达到对不同数据类型的delete
  Object *p = new 0bject(10); 
  op(p);

  p = new Object[10];
  arop(p) ;
}

了解了Deletor的用法,再来重新编写my_unique_ptr

//额外传入 _Dx来接收MyDeletor,使得my_unique_ptr能通过MyDeletor来delete不同数据类型
//下面的函数模板用来处理常规数据类型
template<class _Tyclass _Dx = MyDeletor<_Ty>> 
class my_unique_ptr
{
public:
//using是c11的新用法,等价于typedef
//如using pointer = _Ty*等价于typedef _Ty* pointer
using pointer = _Ty*;
using element_type = _Ty;
using delete_type = _Dx;
private:
  _Ty* _Ptr;
  _Dx  _mydeletor;//创建_Dx类型的实体_mydeletor


public:
  my_unique_ptr(pointer * p = NULL):_Ptr(p){}
   ~my_unique_ptr()
   { 
     if( _Ptr !=NULL)
    {
       _mydeletor(_Ptr);
       _Ptr = NULL;
     }
   }
 
 my_unique_ptr(const my_unique_ptr&) = delete; //禁用拷贝构造
 my_unique_ptr& operator=(const my_unique_ptr&) = delete;//禁用了赋值语句重载

//获取deletor
  _Dx& get_deletor() const
  { return _mydeletor;}
  const  _Dx& get_deletor() const
  { return _mydeletor;}
   _Ty* get()const
   { return _Ptr;}
   
   
   _Ty& operator* () const
    { return*_Ptr; }
  
   _Ty* operator->() const
    { return _Ptr; }

    operator bool()
    { return _Ptr != nullptr } 
     
     //release函数,使指针不再指向原空间,返回原空间地址
    _Ty* release()
    {
      _Ty* old = _Ptr;
      _Ptr = nullptr;
      rerturn old;
    }
    //重置函数,重新定向_Ptr的指向,并将其旧的所拥有的资源释放
   void reset( _Ty* _P = nullptr)
   {
      pointer old = _Ptr;
      _Ptr = _P; 
      if(old != nullptr)
      _mydeletor(old);
   }
   void swap(my_unique_ptr _Y)
   {
     std::swap(_Ptr,_y._Ptr);
     std::swap(_mydeletor._Y._mydeletor);
   }

  my_auto_ptr& operalor=(const my_auto_ptr& _Y)
  {
    if (this == &_Y) return *this;
    if (_Owns)
    {  delete _Ptr; }
    _Owns = _Y._Owns;
    _Ptr = _Y.release() ;
    return 0;
  }

 
 
  //移动构造
  my_unique_ptr(my_unique_ptr&&_Y)
  {
   Ptr = _Y._Ptr;
   _Y._Ptr = nullptr;
  }
   //移动赋值
 my_unique_ptr& operator=(my_unique_ptr&&_Y)
  {
   if(this != &_Y)
   reset(_Y.releas());
   
   return *this;
  }

  
};
//下面的函数模板用来处理数组数据类型
template<class _Tyclass _Dx> 
class my_unique_ptr<_Ty[]._Dx>
{
···;//···表示其余各部分与上例相同

 //额外增加一个对数组下标访问的重载
  _Ty& operator[](size_t Idx) const
  {
     return _Ptr[_Idx];
  }
};
int main()
{
 std::unique_ptr<Objcect> pobja (new Object(10));
 
  std::unique_ptr<Object[]> pobjb(new Object[10]);

make_unique

int main {

std::unique_ptr<Object> op(new Object(10));
std::unique_ptr<Object> op2 = std::make_unique<Object>(100);//两种构建方式等价
;
return 0;
}

1.4shared_ptr(共享型智能指针)的使用&线程安全

在这里插入图片描述

use_count返回引用计数

int main ()
{
std::shared_ptr<Object> sp1(new Object(10)) ;
std::shared_ptr<Object> sp2 = std::make_shared<Object>(20);

std::shared_ptr<Object> sp3;

cout<<sp1.use_count()<<endl; //1
cout<<sp2.use_count()<<endl; //1
cout<<sp3.use_count()<<endl; //0

}

int main ()
{
std::shared_ptr<Object> sp1(new Object(10)) ;
std::shared_ptr<Object> sp2 = std::make_shared<Object>(20);
std::shared_ptr<Object> sp3;

sp3 = sp1;//如果sp3以sp1为参数进行拷贝构造初始化其计数变化与下例相同

cout<<sp1.use_count()<<endl; //2
cout<<sp2.use_count()<<endl; //1
cout<<sp3.use_count()<<endl; //2

}

operator bool 判断指针是否有指向对象

实现如下operatorbool () const { return ptr != nullptr; }


int main ()
{
std::shared_ptr<Object> sp1(new Object(10)) ;

std::shared_ptr<Object> sp2;

if(sp1); //1
if(sp2); //0
return 0;

}

reset 替换指向对象

void reset( _Ty* p = nullptr)
{
if (this->ptr != nullptr & --this->ptr->ref == 0)
  mDclctor(ptr);
  ptr = new RefCnt<_ Ty>(p);
}

线程安全

在多线程中,使用atomic 原子操作来防止+,-等非原子操作所引发的异常
include<atomic>是包含原子操作的库

weak_ptr的引入
在这里插入图片描述
如图,在上例中,因实例之间的循环套用,导致其生命周期结束时,计数器仍未减至0

1.5weak_ptr

在这里插入图片描述

weak_ptr 作为弱引用指针不具备对对象的引用(该功能由强引用指针实现),其仅指向引用计数器 RefCnt,并且以此来解决环形引用的问题
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
对循环问题的解决
在这里插入图片描述

举报

相关推荐

【C++】智能指针

C++——智能指针

C++ 智能指针

C++智能指针

C++ 11 智能指针

C++进阶 智能指针

0 条评论