欢迎来到DIVCSS5查找CSS资料与学习DIV CSS布局技术!
  C++11智能指针原理和实现
 
  一、智能指针起因
 
  在C++中,动态内存的管理是由程序员自己申请和释放的,用一对运算符完成:new和delete。
 
  new:在动态内存中为对象分配一块空间并返回一个指向该对象的指针;
 
  delete:指向一个动态独享的指针,销毁对象,并释放与之关联的内存。
 
  使用堆内存是非常频繁的操作,容易造成堆内存泄露、二次释放等问题,为了更加容易和更加安全的使用动态内存,C++11中引入了智能指针的概念,方便管理堆内存,使得自动、异常安全的对象生存期管理可行。智能指针主要思想是RAII思想,“使用对象管理资源”,在类的构造函数中获取资源,在类的析构函数中释放资源。智能指针的行为类似常规指针,重要的区别是它负责自动释放所指向的对象。
 
  RAII是ResourceAcquisitionIsInitialization的简称,即资源获取就是初始化:
 
  1.定义一个类来封装资源的分配与释放;
 
  2.构造函数中完成资源的分配及初始化;
 
  3.析构函数中完成资源的清理,可以保证资源的正确初始化和释放;
 
  4.如果对象是用声明的方式在栈上创建局部对象,那么RAII机制就会正常工作,当离开作用域对象会自动销毁而调用析构函数释放资源。
 
  二、智能指针类型
 
  智能指针在C++11版本之后提供,包含在头文件<memory>中,标准命名std空间下,有auto_ptr、shared_ptr、weak_ptr、unique_ptr四种,其中auto_ptr已被弃用。
 
  auto_ptr:拥有严格对象所有权语义的智能指针;
 
  shared_ptr:拥有共享对象所有权语义的智能指针;
 
  weak_ptr:到shared_ptr所管理对象的弱引用;
 
  unique_ptr:拥有独有对象所有权语义的智能指针。
 
  2.1auto_ptr
 
  auto_ptr是通过由new表达式获得的对象,并在auto_ptr自身被销毁时删除该对象的智能指针,它可用于为动态分配的对象提供异常安全、传递动态分配对象的所有权给函数和从函数返回动态分配的对象,是一个轻量级的智能指针,适合用来管理生命周期比较短或者不会被远距离传递的动态对象,最好是局限于某个函数内部或者是某个类的内部。
 
  声明:
 
  template<classT>classauto_ptr;
 
  template<>classauto_ptr<void>;//对类型void特化
 
  成员函数:
 
  (1)get:获得内部对象的指针;
 
  (2)release:释放被管理对象的所有权,将内部指针置为空,返回内部对象的指针,此指针需要手动释放;
 
  (3)reset:销毁内部对象并接受新的对象的所有权;
 
  (4)operator=:从另一auto_ptr转移所有权;
 
  (5)operator*和operator->:访问被管理对象。
 
  注意事项:
 
  (1)其构造函数被声明为explicit,因此不能使用赋值运算符对其赋值,即不能使用类似这样的形式auto_ptr<int>p=newint;
 
  (2)auto_ptr的对象所有权是独占性的,使用拷贝构造和赋值操作符时,会造成对象所有权的转移,被拷贝对象在拷贝过程中被修改;
 
  (3)基于第二条,因此不能将auto_ptr放入到标准容器中或作为容器的成员;
 
  (4)auto_ptr不能指向数组,释放时无法确定是数组指针还是普通指针;
 
  (5)不能把一个原生指针交给两个智能指针对象管理,对其它智能指针也是如此。
 
  auto_ptr是最早期的智能指针,在C++11中已被弃用,C++17中移除,建议使用unique_ptr代替auto_ptr。
 
  简单实现:
 
  1template<classT>
 
  2classAutoPointer
 
  3{
 
  4public:
 
  5AutoPointer(T*ptr)
 
  6:mPointer(ptr){}
 
  7
 
  8AutoPointer(AutoPointer<T>&other)
 
  9{
 
  10mPointer=other.mPointer;//管理权进行转移
 
  11other.mPointer=NULL;
 
  12}
 
  13
 
  14AutoPointer&operator=(AutoPointer<T>&other)
 
  15{
 
  16if(this!=&other)
 
  17{
 
  18deletemPointer;
 
  19mPointer=other.mPointer;//管理权进行转移
 
  20other.mPointer=NULL;
 
  21}
 
  22
 
  23return*this;
 
  24}
 
  25
 
  26~AutoPointer()
 
  27{
 
  28deletemPointer;
 
  29}
 
  30
 
  31T&operator*()
 
  32{
 
  33return*mPointer;
 
  34}
 
  35
 
  36T*operator->()
 
  37{
 
  38returnmPointer;
 
  39}
 
  40
 
  41private:
 
  42
 
  43T*mPointer;
 
  44};
 
  2.2shared_ptr
 
  shared_ptr多个指针指向相同的对象,也叫共享指针。shared_ptr采用了引用计数的方式,更好地解决了赋值与拷贝的问题,每一个shared_ptr的拷贝都指向相同的内存,每拷贝一次内部的引用计数加1,每析构一次内部的引用计数减1,为0时自动删除所指向的堆内存。shared_ptr内部的引用计数是线程安全的,但是对象的读取时需要加锁。
 
  声明:
 
  template<classT>classshared_ptr;
 
  成员函数:
 
  (1)get:获得内部对象的指针;
 
  (2)swap:交换所管理的对象;
 
  (3)reset:替换所管理的对象;
 
  (4)use_count:返回shared_ptr所指对象的引用计数;
 
  (5)operator*和operator->:解引用存储的对象指针;
 
  (6)operator=:对shared_ptr赋值;
 
  (7)operatorbool:检查是否有关联的管理对象;
 
  (8)owner_before:提供基于拥有者的共享指针排序。
 
  交换:std::swap(std::shared_ptr)特化的swap算法用于交换两个智能指针。
 
  初始化:通过构造函数传入指针初始化,也可以使用std::make_shared或std::allocate_shared函数初始化。
 
  注意事项:
 
  (1)不能将指针直接赋值给一个智能指针,一个是类,一个是指针。不能使用类似这样的形式shared_ptr<int>p=newint;
 
  (2)避免循环引用,这是shared_ptr的一个最大陷阱,导致内存泄漏,这一点在weak_ptr中将得到完善;
 
  (3)管理数组指针时,需要制定Deleter以使用delete[]操作符销毁内存,shared_ptr并没有针对数组的特化版本;
 
  (4)不能把一个原生指针交给两个智能指针对象管理,对其它智能指针也是如此。
 
  简单实现:
 
  1template<typenameT>
 
  2classSharedPointer
 
  3{
 
  4private:
 
  5
 
  6classImplement
 
  7{
 
  8public:
 
  9Implement(T*p):mPointer(p),mRefs(1){}
 
  10~Implement(){deletemPointer;}
 
  11
 
  12T*mPointer;//实际指针
 
  13size_tmRefs;//引用计数
 
  14};
 
  15
 
  16Implement*mImplPtr;
 
  17
 
  18public:
 
  19
 
  20explicitSharedPointer(T*p)
 
  21:mImplPtr(newImplement(p)){}
 
  22
 
  23~SharedPointer()
 
  24{
 
  25decrease();//计数递减
 
  26}
 
  27
 
  28SharedPointer(constSharedPointer&other)
 
  29:mImplPtr(other.mImplPtr)
 
  30{
 
  31increase();//计数递增
 
  32}
 
  33
 
  34SharedPointer&operator=(constSharedPointer&other)
 
  35{
 
  36if(mImplPtr!=other.mImplPtr)//避免自赋值
 
  37{
 
  38decrease();
 
  39mImplPtr=other.mImplPtr;
 
  40increase();
 
  41}
 
  42
 
  43return*this;
 
  44}
 
  45
 
  46T*operator->()const
 
  47{
 
  48returnmImplPtr->mPointer;
 
  49}
 
  50
 
  51T&operator*()const
 
  52{
 
  53return*(mImplPtr->mPointer);
 
  54}
 
  55
 
  56private:
 
  57
 
  58voiddecrease()
 
  59{
 
  60if(--(mImplPtr->mRefs)==0)
 
  61{
 
  62deletemImplPtr;
 
  63}
 
  64}
 
  65
 
  66voidincrease()
 
  67{
 
  68++(mImplPtr->mRefs);
 
  69}
 
  70};
 
  2.3weak_ptr
 
  weak_ptr是为了配合shared_ptr而引入的一种智能指针,用于专门解决shared_ptr循环引用的问题,因为它不具有普通指针的行为,没有重载operator*和->,它的最大作用在于协助shared_ptr工作,像旁观者那样观测资源的使用情况。weak_ptr可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。weak_ptr可以使用一个非常重要的成员函数lock(),从被观测的shared_ptr获得一个可用的shared_ptr对象,从而操作资源。
 
  声明:
 
  template<classT>classweak_ptr;
 
  成员函数:
 
  (1)swap:交换所管理的对象;
 
  (2)reset:替换所管理的对象;
 
  (3)use_count:返回shared_ptr所指对象的引用计数;
 
  (4)operator=:对shared_ptr赋值;
 
  (5)expired:检查被引用的对象是否已删除;
 
  (6)owner_before:提供基于拥有者的共享指针排序;
 
  (7)lock:创建管理被引用的对象的shared_ptr。
 
  交换:std::swap(std::weak_ptr)特化的swap算法用于交换两个智能指针。
 
  注意事项:
 
  (1)不能将指针直接赋值给一个智能指针,一个是类,一个是指针。不能使用类似这样的形式shared_ptr<int>p=newint;
 
  (2)不能把一个原生指针交给两个智能指针对象管理,对其它智能指针也是如此。
 
  简单实现:weak_ptr的典型实现存储二个指针,即指向控制块的指针和作为构造来源的shared_ptr的存储指针。
 
  以下是VC的源码实现:
 
  1template<class_Ty>
 
  2classweak_ptr
 
  3:public_Ptr_base<_Ty>
 
  4{//classforpointertoreferencecountedresource
 
  5typedeftypename_Ptr_base<_Ty>::_Elem_Elem;
 
  6
 
  7public:
 
  8weak_ptr()
 
  9{//constructemptyweak_ptrobject
 
  10}
 
  11
 
  12template<class_Ty2>
 
  13weak_ptr(constshared_ptr<_Ty2>&_Other,
 
  14typenameenable_if<is_convertible<_Ty2*,_Ty*>::value,
 
  15void*>::type*=0)
 
  16{//constructweak_ptrobjectforresourceownedby_Other
 
  17this->_Resetw(_Other);
 
  18}
 
  19
 
  20weak_ptr(constweak_ptr&_Other)
 
  21{//constructweak_ptrobjectforresourcepointedtoby_Other
 
  22this->_Resetw(_Other);
 
  23}
 
  24
 
  25template<class_Ty2>
 
  26weak_ptr(constweak_ptr<_Ty2>&_Other,
 
  27typenameenable_if<is_convertible<_Ty2*,_Ty*>::value,
 
  28void*>::type*=0)
 
  29{//constructweak_ptrobjectforresourcepointedtoby_Other
 
  30this->_Resetw(_Other);
 
  31}
 
  32
 
  33~weak_ptr()
 
  34{//releaseresource
 
  35this->_Decwref();
 
  36}
 
  37
 
  38weak_ptr&operator=(constweak_ptr&_Right)
 
  39{//assignfrom_Right
 
  40this->_Resetw(_Right);
 
  41return(*this);
 
  42}
 
  43
 
  44template<class_Ty2>
 
  45weak_ptr&operator=(constweak_ptr<_Ty2>&_Right)
 
  46{//assignfrom_Right
 
  47this->_Resetw(_Right);
 
  48return(*this);
 
  49}
 
  50
 
  51template<class_Ty2>
 
  52weak_ptr&operator=(shared_ptr<_Ty2>&_Right)
 
  53{//assignfrom_Right
 
  54this->_Resetw(_Right);
 
  55return(*this);
 
  56}
 
  57
 
  58voidreset()
 
  59{//releaseresource,converttonullweak_ptrobject
 
  60this->_Resetw();
 
  61}
 
  62
 
  63voidswap(weak_ptr&_Other)
 
  64{//swappointers
 
  65this->_Swap(_Other);
 
  66}
 
  67
 
  68boolexpired()const
 
  69{//returntrueifresourcenolongerexists
 
  70return(this->_Expired());
 
  71}
 
  72
 
  73shared_ptr<_Ty>lock()const
 
  74{//converttoshared_ptr
 
  75return(shared_ptr<_Elem>(*this,false));
 
  76}
 
  77};
 
  2.4unique_ptr
 
  unique_ptr实际上相当于一个安全性增强了的auto_ptr。unique_ptr是通过指针占有并管理另一对象,并在unique_ptr离开作用域时释放该对象的智能指针。unique_ptr的使用标志着控制权的转移,同一时刻只能有一个unique_ptr指向给定对象,通过禁止拷贝语义、只有移动语义来实现。相比与原始指针unique_ptr用于其RAII的特性,使得在出现异常的情况下,动态资源能得到释放。
 
  声明:
 
  template<classT,classDeleter=std::default_delete<T>>classunique_ptr;
 
  template<classT,classDeleter>classunique_ptr<T[],Deleter>;//管理数组指针
 
  成员函数:
 
  (1)get:返回指向被管理对象的指针;
 
  (2)get_deleter:返回用于析构被管理对象7的删除器;
 
  (3)swap:交换所管理的对象;
 
  (4)reset:替换所管理的对象;
 
  (5)release:返回一个指向被管理对象的指针,并释放所有权;
 
  (6)operatorbool:检查是否有关联的被管理对象;
 
  (7)operator=:为unique_ptr赋值;
 
  (8)operator*和operator->:解引用存储的对象指针。
 
  注意事项:
 
  (1)不能将指针直接赋值给一个智能指针,一个是类,一个是指针。不能使用类似这样的形式shared_ptr<int>p=newint;
 
  (2)不能把一个原生指针交给两个智能指针对象管理,对其它智能指针也是如此。
 
  简单实现:
 
  1//defaultdeleterforunique_ptr
 
  2template<typenameT>
 
  3structDefaultDeleter
 
  4{
 
  5voidoperator()(T*p)
 
  6{
 
  7if(p)
 
  8{
 
  9deletep;
 
  10p=NULL;
 
  11}
 
  12}
 
  13};
 
  14
 
  15template<typenameT,typenameDeleter=DefaultDeleter<T>>
 
  16classunique_ptr
 
  17{
 
  18public:
 
  19
 
  20//construct
 
  21unique_ptr(T*pT=NULL);
 
  22
 
  23//destroy
 
  24~unique_ptr();
 
  25
 
  26private:
 
  27
 
  28//notallowcopyable
 
  29unique_ptr(constunique_ptr&);
 
  30
 
  31unique_ptr&operator=(constunique_ptr&);
 
  32
 
  33public:
 
  34
 
  35//reset
 
  36voidreset(T*p);
 
  37
 
  38//releasetheownofthepointer
 
  39T*release();
 
  40
 
  41//getthepointer
 
  42T*get();
 
  43
 
  44//convertunique_ptrtobool
 
  45operatorbool()const;
 
  46
 
  47//overloadforoperator*
 
  48T&operator*();
 
  49
 
  50//overloadforoperator->
 
  51T*operator->();
 
  52
 
  53private:
 
  54
 
  55T*m_pT;//pointer
 
  56
 
  57Deleterm_deleter;//deleter
 
  58
 
  59voiddel();//calldeleter
 
  60};
 
  61
 
  62
 
  63template<typenameT,typenameDeleter>
 
  64unique_ptr<T,Deleter>::unique_ptr(T*pT):m_pT(pT)
 
  65{
 
  66
 
  67}
 
  68
 
  69template<typenameT,typenameDeleter>
 
  70unique_ptr<T,Deleter>::~unique_ptr()
 
  71{
 
  72del();
 
  73}
 
  74
 
  75template<typenameT,typenameDeleter>
 
  76voidunique_ptr<T,Deleter>::del()
 
  77{
 
  78if(*this)
 
  79{
 
  80m_deleter(m_pT);
 
  81m_pT=NULL;
 
  82}
 
  83}
 
  84
 
  85template<typenameT,typenameDeleter>
 
  86T*unique_ptr<T,Deleter>::get()
 
  87{
 
  88returnm_pT;
 
  89}
 
  90
 
  91template<typenameT,typenameDeleter>
 
  92voidunique_ptr<T,Deleter>::reset(T*p)
 
  93{
 
  94del();
 
  95m_pT=p;
 
  96}
 
  97
 
  98template<typenameT,typenameDeleter>
 
  99T*unique_ptr<T,Deleter>::release()
 
  100{
 
  101T*p=m_pT;
 
  102m_pT=NULL;
 
  103returnp;
 
  104}
 
  105
 
  106template<typenameT,typenameDeleter>
 
  107unique_ptr<T,Deleter>::operatorbool()const
 
  108{
 
  109returnNULL!=m_pT;
 
  110}
 
  111
 
  112template<typenameT,typenameDeleter>
 
  113T&unique_ptr<T,Deleter>::operator*()
 
  114{
 
  115return*m_pT;
 
  116}
 
  117
 
  118template<typenameT,typenameDeleter>
 
  119T*unique_ptr<T,Deleter>::operator->()
 
  120{
 
  121returnm_pT;
 
  122}
 
  三、总结
 
  智能指针就是模拟指针动作的类,一般智能指针都会重载->和*操作符。智能指针主要作用是管理动态内存的释放。
 
  1.不要使用std::auto_ptr;
 
  2.当你需要一个独占资源所有权的指针,且不允许任何外界访问,请使用std::unique_ptr;
 
  3.当你需要一个共享资源所有权的指针,请使用std::shared_ptr;
 
  4.当你需要一个能访问资源,但不控制其生命周期的指针,请使用std::weak_ptr;
 
  5.不能把一个原生指针交给两个智能指针对象管理。

如需转载,请注明文章出处和来源网址:http://www.divcss5.com/html/h56750.shtml