C++11智能指针详解
一、智能指针起因
在c++中,动态内存的管理是由程序员自己申请和释放的,用一对运算符完成:new和delete。
new:在动态内存中为对象分配一块空间并返回一个指向该对象的指针;
delete:指向一个动态独享的指针,销毁对象,并释放与之关联的内存。
使用堆内存是非常频繁的操作,容易造成堆内存泄露、二次释放等问题,为了更加容易和更加安全的使用动态内存,c++11中引入了智能指针的概念,方便管理堆内存,使得自动、异常安全的对象生存期管理可行。智能指针主要思想是raii思想,“使用对象管理资源”,在类的构造函数中获取资源,在类的析构函数中释放资源。智能指针的行为类似常规指针,重要的区别是它负责自动释放所指向的对象。
raii是resource acquisition is initialization的简称,即资源获取就是初始化:
1.定义一个类来封装资源的分配与释放;
2.构造函数中完成资源的分配及初始化;
3.析构函数中完成资源的清理,可以保证资源的正确初始化和释放;
4.如果对象是用声明的方式在栈上创建局部对象,那么raii机制就会正常工作,当离开作用域对象会自动销毁而调用析构函数释放资源。
二、智能指针类型
智能指针在c++11版本之后提供,包含在头文件<memory>中,标准命名std空间下,有auto_ptr、shared_ptr、weak_ptr、unique_ptr四种,其中auto_ptr已被弃用。
:拥有严格对象所有权语义的智能指针;
:拥有共享对象所有权语义的智能指针;
:到 shared_ptr 所管理对象的弱引用;
:拥有独有对象所有权语义的智能指针。
2.1 auto_ptr
auto_ptr是通过由 new 表达式获得的对象,并在auto_ptr自身被销毁时删除该对象的智能指针,它可用于为动态分配的对象提供异常安全、传递动态分配对象的所有权给函数和从函数返回动态分配的对象,是一个轻量级的智能指针,适合用来管理生命周期比较短或者不会被远距离传递的动态对象,最好是局限于某个函数内部或者是某个类的内部。
声明:
template< class t > class auto_ptr;
template<> class auto_ptr<void>; // 对类型void特化
成员函数:
(1) : 获得内部对象的指针;
(2) :释放被管理对象的所有权,将内部指针置为空,返回内部对象的指针,此指针需要手动释放;
(3) :销毁内部对象并接受新的对象的所有权;
(4) operator=:从另一auto_ptr转移所有权;
(5) 和:访问被管理对象。
注意事项:
(1) 其构造函数被声明为explicit,因此不能使用赋值运算符对其赋值,即不能使用类似这样的形式 auto_ptr<int> p = new int;
(2) auto_ptr 的对象所有权是独占性的,使用拷贝构造和赋值操作符时,会造成对象所有权的转移,被拷贝对象在拷贝过程中被修改;
(3) 基于第二条,因此不能将auto_ptr放入到标准容器中或作为容器的成员;
(4) auto_ptr不能指向数组,释放时无法确定是数组指针还是普通指针;
(5) 不能把一个原生指针交给两个智能指针对象管理,对其它智能指针也是如此。
auto_ptr是最早期的智能指针,在c++11 中已被弃用,c++17 中移除,建议使用unique_ptr代替auto_ptr。
简单实现:
1 template<class t> 2 class autopointer 3 { 4 public: 5 autopointer(t* ptr) 6 :mpointer(ptr){} 7 8 autopointer(autopointer<t>& other) 9 { 10 mpointer= other.mpointer; //管理权进行转移 11 other.mpointer= null; 12 } 13 14 autopointer& operator = (autopointer<t>& other) 15 { 16 if(this != &other) 17 { 18 delete mpointer; 19 mpointer = other.mpointer; //管理权进行转移 20 other.mpointer= null; 21 } 22 23 return *this; 24 } 25 26 ~autopointer() 27 { 28 delete mpointer; 29 } 30 31 t& operator * () 32 { 33 return *mpointer; 34 } 35 36 t* operator -> () 37 { 38 return mpointer; 39 } 40 41 private: 42 43 t* mpointer; 44 };
2.2 shared_ptr
shared_ptr多个指针指向相同的对象,也叫共享指针。shared_ptr采用了引用计数的方式,更好地解决了赋值与拷贝的问题,每一个shared_ptr的拷贝都指向相同的内存,每拷贝一次内部的引用计数加1,每析构一次内部的引用计数减1,为0时自动删除所指向的堆内存。shared_ptr内部的引用计数是线程安全的,但是对象的读取时需要加锁。
声明:
template< class t > class shared_ptr;
成员函数:
(1) : 获得内部对象的指针;
(2) :交换所管理的对象;
(3) :替换所管理的对象;
(4) :返回shared_ptr所指对象的引用计数;
(5) 和:解引用存储的对象指针;
(6) operator=:对shared_ptr赋值;
(7) :检查是否有关联的管理对象;
(8) :提供基于拥有者的共享指针排序。
交换: 特化的swap算法用于交换两个智能指针。
初始化:通过构造函数传入指针初始化,也可以使用 或 函数初始化。
注意事项:
(1) 不能将指针直接赋值给一个智能指针,一个是类,一个是指针。不能使用类似这样的形式 shared_ptr<int> p = new int;
(2) 避免循环引用,这是shared_ptr的一个最大陷阱,导致内存泄漏,这一点在weak_ptr中将得到完善;
(3) 管理数组指针时,需要制定deleter以使用delete[]操作符销毁内存,shared_ptr并没有针对数组的特化版本;
(4) 不能把一个原生指针交给两个智能指针对象管理,对其它智能指针也是如此。
简单实现:
1 template <typename t> 2 class sharedpointer 3 { 4 private: 5 6 class implement 7 { 8 public: 9 implement(t* p) : mpointer(p), mrefs(1){} 10 ~implement(){ delete mpointer;} 11 12 t* mpointer; //实际指针 13 size_t mrefs; // 引用计数 14 }; 15 16 implement* mimplptr; 17 18 public: 19 20 explicit sharedpointer(t* p) 21 : mimplptr(new implement(p)){} 22 23 ~sharedpointer() 24 { 25 decrease(); // 计数递减 26 } 27 28 sharedpointer(const sharedpointer& other) 29 : mimplptr(other.mimplptr) 30 { 31 increase(); // 计数递增 32 } 33 34 sharedpointer& operator = (const sharedpointer& other) 35 { 36 if(mimplptr != other.mimplptr) // 避免自赋值 37 { 38 decrease(); 39 mimplptr = other.mimplptr; 40 increase(); 41 } 42 43 return *this; 44 } 45 46 t* operator -> () const 47 { 48 return mimplptr->mpointer; 49 } 50 51 t& operator * () const 52 { 53 return *(mimplptr->mpointer); 54 } 55 56 private: 57 58 void decrease() 59 { 60 if(--(mimplptr->mrefs) == 0) 61 { 62 delete mimplptr; 63 } 64 } 65 66 void increase() 67 { 68 ++(mimplptr->mrefs); 69 } 70 };
2.3 weak_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< class t > class weak_ptr;
成员函数:
(1) :交换所管理的对象;
(2) :替换所管理的对象;
(3) :返回shared_ptr所指对象的引用计数;
(4) operator=:对shared_ptr赋值;
(5) :检查被引用的对象是否已删除;
(6) :提供基于拥有者的共享指针排序;
(7) :创建管理被引用的对象的shared_ptr。
交换: 特化的swap算法用于交换两个智能指针。
注意事项:
(1) 不能将指针直接赋值给一个智能指针,一个是类,一个是指针。不能使用类似这样的形式 shared_ptr<int> p = new int;
(2) 不能把一个原生指针交给两个智能指针对象管理,对其它智能指针也是如此。
简单实现:weak_ptr的典型实现存储二个指针,即指向控制块的指针和作为构造来源的shared_ptr的存储指针。
以下是vc的源码实现:
1 template<class _ty> 2 class weak_ptr 3 : public _ptr_base<_ty> 4 { // class for pointer to reference counted resource 5 typedef typename _ptr_base<_ty>::_elem _elem; 6 7 public: 8 weak_ptr() 9 { // construct empty weak_ptr object 10 } 11 12 template<class _ty2> 13 weak_ptr(const shared_ptr<_ty2>& _other, 14 typename enable_if<is_convertible<_ty2 *, _ty *>::value, 15 void *>::type * = 0) 16 { // construct weak_ptr object for resource owned by _other 17 this->_resetw(_other); 18 } 19 20 weak_ptr(const weak_ptr& _other) 21 { // construct weak_ptr object for resource pointed to by _other 22 this->_resetw(_other); 23 } 24 25 template<class _ty2> 26 weak_ptr(const weak_ptr<_ty2>& _other, 27 typename enable_if<is_convertible<_ty2 *, _ty *>::value, 28 void *>::type * = 0) 29 { // construct weak_ptr object for resource pointed to by _other 30 this->_resetw(_other); 31 } 32 33 ~weak_ptr() 34 { // release resource 35 this->_decwref(); 36 } 37 38 weak_ptr& operator=(const weak_ptr& _right) 39 { // assign from _right 40 this->_resetw(_right); 41 return (*this); 42 } 43 44 template<class _ty2> 45 weak_ptr& operator=(const weak_ptr<_ty2>& _right) 46 { // assign from _right 47 this->_resetw(_right); 48 return (*this); 49 } 50 51 template<class _ty2> 52 weak_ptr& operator=(shared_ptr<_ty2>& _right) 53 { // assign from _right 54 this->_resetw(_right); 55 return (*this); 56 } 57 58 void reset() 59 { // release resource, convert to null weak_ptr object 60 this->_resetw(); 61 } 62 63 void swap(weak_ptr& _other) 64 { // swap pointers 65 this->_swap(_other); 66 } 67 68 bool expired() const 69 { // return true if resource no longer exists 70 return (this->_expired()); 71 } 72 73 shared_ptr<_ty> lock() const 74 { // convert to shared_ptr 75 return (shared_ptr<_elem>(*this, false)); 76 } 77 };
2.4 unique_ptr
unique_ptr实际上相当于一个安全性增强了的auto_ptr。unique_ptr是通过指针占有并管理另一对象,并在unique_ptr离开作用域时释放该对象的智能指针。unique_ptr的使用标志着控制权的转移,同一时刻只能有一个unique_ptr指向给定对象,通过禁止拷贝语义、只有移动语义来实现。相比与原始指针unique_ptr用于其raii的特性,使得在出现异常的情况下,动态资源能得到释放。
声明:
template< class t, class deleter = std::default_delete<t> > class unique_ptr;
template< class t, class deleter> class unique_ptr<t[], deleter>; // 管理数组指针
成员函数:
(1) : 返回指向被管理对象的指针;
(2) :返回用于析构被管理对象7的删除器;
(3) :交换所管理的对象;
(4) :替换所管理的对象;
(5) :返回一个指向被管理对象的指针,并释放所有权;
(6) :检查是否有关联的被管理对象;
(7) operator=:为unique_ptr赋值;
(8) 和:解引用存储的对象指针。
注意事项:
(1) 不能将指针直接赋值给一个智能指针,一个是类,一个是指针。不能使用类似这样的形式 shared_ptr<int> p = new int;
(2) 不能把一个原生指针交给两个智能指针对象管理,对其它智能指针也是如此。
简单实现:
1 //default deleter for unique_ptr 2 template<typename t> 3 struct defaultdeleter 4 { 5 void operator () (t *p) 6 { 7 if(p) 8 { 9 delete p; 10 p = null; 11 } 12 } 13 }; 14 15 template<typename t, typename deleter = defaultdeleter<t>> 16 class unique_ptr 17 { 18 public: 19 20 // construct 21 unique_ptr(t *pt = null); 22 23 // destroy 24 ~unique_ptr(); 25 26 private: 27 28 // not allow copyable 29 unique_ptr(const unique_ptr &); 30 31 unique_ptr&operator=(const unique_ptr &); 32 33 public: 34 35 // reset 36 void reset(t *p); 37 38 // release the own of the pointer 39 t* release(); 40 41 // get the pointer 42 t* get(); 43 44 // convert unique_ptr to bool 45 operator bool() const; 46 47 // overload for operator * 48 t& operator * (); 49 50 // overload for operator -> 51 t* operator -> (); 52 53 private: 54 55 t *m_pt; //pointer 56 57 deleter m_deleter; //deleter 58 59 void del(); //call deleter 60 }; 61 62 63 template<typename t, typename deleter> 64 unique_ptr<t, deleter>::unique_ptr(t *pt) :m_pt(pt) 65 { 66 67 } 68 69 template<typename t, typename deleter> 70 unique_ptr<t, deleter>::~unique_ptr() 71 { 72 del(); 73 } 74 75 template<typename t, typename deleter> 76 void unique_ptr<t, deleter>::del() 77 { 78 if(*this) 79 { 80 m_deleter(m_pt); 81 m_pt = null; 82 } 83 } 84 85 template<typename t, typename deleter> 86 t* unique_ptr<t, deleter>::get() 87 { 88 return m_pt; 89 } 90 91 template<typename t, typename deleter> 92 void unique_ptr<t, deleter>::reset(t *p) 93 { 94 del(); 95 m_pt = p; 96 } 97 98 template<typename t, typename deleter> 99 t* unique_ptr<t, deleter>::release() 100 { 101 t *p = m_pt; 102 m_pt = null; 103 return p; 104 } 105 106 template<typename t, typename deleter> 107 unique_ptr<t, deleter>::operator bool() const 108 { 109 return null != m_pt; 110 } 111 112 template<typename t, typename deleter> 113 t& unique_ptr<t, deleter>::operator * () 114 { 115 return *m_pt; 116 } 117 118 template<typename t, typename deleter> 119 t* unique_ptr<t, deleter>::operator -> () 120 { 121 return m_pt; 122 }
三、总结
智能指针就是模拟指针动作的类,一般智能指针都会重载 -> 和 * 操作符。智能指针主要作用是管理动态内存的释放。
1.不要使用std::auto_ptr;
2.当你需要一个独占资源所有权的指针,且不允许任何外界访问,请使用std::unique_ptr;
3.当你需要一个共享资源所有权的指针,请使用std::shared_ptr;
4.当你需要一个能访问资源,但不控制其生命周期的指针,请使用std::weak_ptr;
5.不能把一个原生指针交给两个智能指针对象管理。