一、智能指针起因

  在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已被弃用。

  auto_ptr:拥有严格对象所有权语义的智能指针;

  shared_ptr:拥有共享对象所有权语义的智能指针;

  weak_ptr:到 shared_ptr 所管理对象的弱引用;

  unique_ptr:拥有独有对象所有权语义的智能指针。

2.1 auto_ptr

  auto_ptr是通过由 new 表达式获得的对象,并在auto_ptr自身被销毁时删除该对象的智能指针,它可用于为动态分配的对象提供异常安全、传递动态分配对象的所有权给函数和从函数返回动态分配的对象,是一个轻量级的智能指针,适合用来管理生命周期比较短或者不会被远距离传递的动态对象,最好是局限于某个函数内部或者是某个类的内部。

  声明:

  template< class T > class auto_ptr;

  template<> class auto_ptr<void>;  // 对类型void特化  

  成员函数:

  (1) get: 获得内部对象的指针;

  (2) release:释放被管理对象的所有权,将内部指针置为空,返回内部对象的指针,此指针需要手动释放;

  (3) reset:销毁内部对象并接受新的对象的所有权;

  (4) operator=:从另一auto_ptr转移所有权;

  (5) operator*operator->:访问被管理对象。

  注意事项:

  (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) get: 获得内部对象的指针;

  (2) swap:交换所管理的对象;

  (3) reset:替换所管理的对象;

  (4) use_count:返回shared_ptr所指对象的引用计数;

  (5) operator*operator->:解引用存储的对象指针;

  (6) operator=:对shared_ptr赋值;

  (7) operator bool:检查是否有关联的管理对象;

  (8) owner_before:提供基于拥有者的共享指针排序。

  交换: std::swap(std::shared_ptr) 特化的swap算法用于交换两个智能指针。

  初始化:通过构造函数传入指针初始化,也可以使用std::make_shared 或 std::allocate_shared 函数初始化。

  注意事项:

  (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) 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 = 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) get: 返回指向被管理对象的指针;

  (2) get_deleter:返回用于析构被管理对象7的删除器;

  (3) swap:交换所管理的对象;

  (4) reset:替换所管理的对象;

  (5) release:返回一个指向被管理对象的指针,并释放所有权;

  (6) operator bool:检查是否有关联的被管理对象;

  (7) operator=:为unique_ptr赋值;

  (8) operator*operator->:解引用存储的对象指针。

  注意事项:

  (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.不能把一个原生指针交给两个智能指针对象管理。

C++智能指针的原理和实现的更多相关文章

  1. C++11中智能指针的原理、使用、实现

    目录 理解智能指针的原理 智能指针的使用 智能指针的设计和实现 1.智能指针的作用 C++程序设计中使用堆内存是非常频繁的操作,堆内存的申请和释放都由程序员自己管理.程序员自己管理堆内存可以提高了程序 ...

  2. C++面试题(四)——智能指针的原理和实现

    C++面试题(一).(二)和(三)都搞定的话,恭喜你来到这里,这基本就是c++面试题的最后一波了.     1,你知道智能指针吗?智能指针的原理.     2,常用的智能指针.     3,智能指针的 ...

  3. C++智能指针 原理、使用与实现

    目录 理解智能指针的原理 智能指针的使用 智能指针的设计和实现 1.智能指针的作用 C++程序设计中使用堆内存是非常频繁的操作,堆内存的申请和释放都由程序员自己管理.程序员自己管理堆内存可以提高了程序 ...

  4. 智能指针shared_ptr的用法

    为了解决C++内存泄漏的问题,C++11引入了智能指针(Smart Pointer). 智能指针的原理是,接受一个申请好的内存地址,构造一个保存在栈上的智能指针对象,当程序退出栈的作用域范围后,由于栈 ...

  5. C++在设计和使用智能指针

    为一个C++用户的.使用指针可以算的上是常态,但在使用过程中.多的时间,可能是由于new要么malloc对象,上次忘记的释放结束(我会犯这样一个错误).内存泄露. 而此时智能指针可能能够帮助我去解决问 ...

  6. c++ 智能指针、函数指针和指针函数

    智能指针: 1.内存泄漏memory leak :是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄漏似乎不会有大的影响,但内存泄漏堆积后的后果就是内存溢出. 2.内存溢出 out of me ...

  7. 【C/C++学院】0904-boost智能指针/boost多线程锁定/哈希库/正則表達式

    boost_array_bind_fun_ref Array.cpp #include<boost/array.hpp> #include <iostream> #includ ...

  8. 智能指针 与 oc中的指针

     智能指针 与 oc中的指针 智能指针的原理及实现 当类中有指针成员时,一般有两种方式来管理指针成员:一是采用值型的方式管理,每个类对象都保留一份指针指向的对象的拷贝:另一种更优雅的方式是使用智能指针 ...

  9. 【C++】智能指针简述(六):智能指针总结及补充

    本文我们主要来总结一下前文介绍过的智能指针相关原理及实现,顺便补充一下前文未提到的shared_ptr删除器部分的内容. 总结: 1.智能指针,通过RAII机制,构造对象时完成资源的初始化,析构对象时 ...

随机推荐

  1. Codeforces Round #139 (Div. 2) 题解

    vp上古场次ing CF225A Dice Tower 1.题目简述: 有 \(n\) 个骰子被叠在了一起.对于每个骰子上的一个数,与它对面的数的和始终为 \(7\) . 你是小明,你只能从正面看这个 ...

  2. Codeforces Round #707 Editorial Div2 题解

    CF1501 Div2 题解 CF1501A 这道题其实是一道英语阅读题,然后样例解释又不清晰,所以我看了好久,首先它告诉了你每个站点的预期到达时间 \(a_i\) ,以及每个站点的预期出发时间 \( ...

  3. asp.net c#整理所有本地的图片一次性保存到SQL表中

    string sql1 = "select distinct tx from tiku where tx is not null"; //检索tx表中所有的不重复的tx值 stri ...

  4. 浅谈MVC设计模式

    摘要:MVC即Model.View.Controller即模型.视图.控制器,它是用一种业务逻辑.数据与界面显示分离的方法来组织代码,将众多的业务逻辑聚集到一个部件里面,在需要改进和个性化定制界面及用 ...

  5. Android系统编程入门系列之界面Activity响应丝滑的传统动画

    上篇文章介绍了应用程序内对用户操作响应的相关方法位置,简单的响应逻辑可以是从一个界面Activity跳转到另一个界面Activity,也可以是某些视图View的相对变化.然而不管是启动一个界面执行新界 ...

  6. 手把手教你实现Android编译期注解

    一.编译期注解在开发中的重要性 从早期令人惊艳的ButterKnife,到后来的以ARouter为首的各种路由框架,再到现在谷歌大力推行的Jetpack组件,越来越多的第三方框架都在使用编译期注解这门 ...

  7. Linux用户提权管理方案

    提权管理方案背景: 如果一个公司有10余个运维或网络安全人员,同时管理服务器,切换到管理员用户时(su - root),必须要有root管理员密码,如果其中一人修改过root密码,其他用户则登录不了, ...

  8. cookie详解(八)

    一.什么是cookie 1.大概了解 什么是 Cookie?针对不同用户,做出不同的响应.,这就是Cookie的原理.是浏览器储存在用户的机器上的(可见免用户名登录)1. Cookie翻译过来是饼干的 ...

  9. jvm源码解读--05 常量池 常量项的解析JVM_CONSTANT_Utf8

    当index=18的时候JVM_CONSTANT_Utf8 case JVM_CONSTANT_Utf8 : { cfs->guarantee_more(2, CHECK); // utf8_l ...

  10. cent os 基本命令一

    命令详情 # man [命令] *********************目录****************************** 一.文件及目录操作 二,vi 三,vim 四,用户操作 五, ...