一、智能指针起因

  在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. 使用 VSCode 搭建 Flutter环境

    概述 编辑器使用 vscode,不再安装 Android Studio. 安装 Git 点击这里 下载并安装 Git 配置 Java 环境 下载和安装 JDK 点击下载 Java SE Develop ...

  2. lucene 类介绍

    lucene中重要的类: IndexWriter:lucene中最重要的类之一,主要用于索引的创建 Analyzer(抽象类):分析器,主要用于分析文本,常用的有StandardAnalyzer分析器 ...

  3. Python+Requests+Re(正则)爬取某糗事百科图片(数据分析一)

    1.博客目前在学习爬虫课程,使用正则表达式来爬取网页的图片信息 2.下面我们一起来回归下Python中的正则使用方式/方法 3.糗事百科图片爬取源码如下: import requestsimport ...

  4. JavaScript学习笔记:你必须要懂的原生JS(一)

    1.原始类型有哪几种?null是对象吗?原始数据类型和复杂数据类型存储有什么区别? 原始类型有6种,分别是undefined,null,bool,string,number,symbol(ES6新增) ...

  5. POJ3179 Corral the Cows题解

    我就是个垃圾--一道水题能写这么长时间-- 首先看到题就想到了二维前缀和+二分边长,但地图边长10000,得离散化. 于是这个离散化就把我搞疯了,淦. 这反映出现在基础知识还是不牢固,相当不牢固. 复 ...

  6. 第九篇 -- cpu的学习

    CPU的核数关系: 总核数 = 物理CPU个数 X 每颗物理CPU的核数 总逻辑CPU数 = 物理CPU个数 X 每颗物理CPU的核数 X 超线程数 Linux中查看CPU数: # 查看物理CPU个数 ...

  7. argparse模块基本用法

    argparse模块基本用法 在 python 编写的程序中,我们经常会看到的 argparse 相关代码,而它究竟怎么使用呢?接招! argparse 是一个命令行参数解析模块 现在提出需求,我需要 ...

  8. Jenkins配置下拉菜单联动效果

    在使用Jenkins集成时,经常需要配置一些环境信息,由于测试.线上.预发布需要切换环境和域名,需要在Jenkins中配置下拉菜单联动效果. 首先选择参数化构建过程,然后首先配置环境,环境分为:测试环 ...

  9. C++11 noexcept 关键字用法学习

    最近学习和写了一个 mint 的板子 ,其中用到了 noexcept 关键字,对这个关键字不太熟悉,便学习一下刘毅学长的文章. C++98 中的异常规范(Exception Specification ...

  10. Vue3学习第一例:Vue3架构入门

    入门 Vue3的教程很少,官方网站实例不好整,另外由于Python的Django也掌握了,学习这个有些让人眼乱.Vue项目创建后,在public目录下面自动生成了一个index.htm,里面有个div ...