一,为什么需要智能指针

智能指针是一种预防型的内存泄漏的解决方案。由于C++没有垃圾回收器机制,所以每次new出来的资源都需要手动的delete,如果没有手动释放,就会造成资源泄漏等问题。因此,为了避免这一问题,C++引入了智能指针,可以较好的解决异常安全等带来的内存泄漏问题。

智能指针的原理:RAII(自动释放资源)+具有指针类似的行为operator*() / operator->()+解决浅拷贝的方式。
所有不同类型的智能指针都包括以下这些内容:
1,RAII:资源可以自动释放
2,具有指针类似的行为:operator*()/operator->()
3,都需要考虑解决浅拷贝的问题

二,RAII
RAII–资源获取时就初始化,将内存的管理交付给了对象,在构造函数中申请资源,在析构函数中释放资源。这样就避免了资源泄漏问题。

简单模拟实现:

template<class T>
class smartptr
{
public:smartptr(T* ptr = nullptr):_ptr(ptr){cout << "smartptr(T* )" << endl;}~smartptr(){cout << "~smartptr(T* )" << endl;if (_ptr){delete _ptr;_ptr = nullptr;}}
private:T* _ptr;
};
void testsmartptr()
{smartptr<int> sp(new int);
}
int main()
{testsmartptr();return 0;
}

RAII的作用:用户不用考虑什么时候释放资源,把释放资源的事情交给了编译器

但这样的简单实现会导致浅拷贝,导致一份资源多次释放

template<class T>
class smartptr
{
public:smartptr(T* ptr = nullptr):_ptr(ptr){cout << "smartptr(T* )" << endl;}~smartptr(){cout << "~smartptr(T* )" << endl;if (_ptr){delete _ptr;_ptr = nullptr;}}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}
private:T* _ptr;
};
void testsmartptr()
{int a = 10;int *pa = &a;int *pb(pa);smartptr<int> sp1(new int);smartptr<int> sp2(sp1);//由于没有拷贝构造函数,所以编译器会调用构造函数。sp1和sp2会指向同一块内存空间
}//在函数结束时,sp1,sp2都会释放内存,这是就会导致同一内存多次释放。
int main()
{testsmartptr();return 0;
}

虽然之前我们所遇到的string类也存在浅拷贝问题,它可以通过深拷贝来解决问题,但是我们这个智能指针却不能通过深拷贝的方式来解决。这是因为string所申请的空间是在类中申请的空间,所以在拷贝构造函数中也只需要在类中为新对象申请一块空间。但是我们所提供的智能指针,它的资源是用户提供的,并不是类自己申请。它没有自己申请的权利,只有释放的权利。因此它不能通过深拷贝的方式进行解决。

三,智能指针的类型

auto_ptr(C++98)

namespace heqing
{//auto_ptr解决的原理就是资源的转移template<class T>class auto_ptr{public://RAIIauto_ptr(T* ptr = nullptr):_ptr(ptr){}~auto_ptr(){if (_ptr){delete _ptr;_ptr = nullptr;}}//指针特性T& operator*(){return *_ptr;}T* operator->(){return _ptr;}//解决浅拷贝(资源的转移)auto_ptr(auto_ptr<T>& ap):_ptr(ap._ptr){ap._ptr = nullptr;}auto_ptr<T>& operator=(auto_ptr<T> ap){if (this != &ap){if (_ptr)//如果当前对象管理资源了,先释放原有的资源{delete _ptr;}_ptr = ap._ptr;//资源转移ap._ptr = nullptr;//ap与资源断开练习}return *this;}private:T* _ptr;};
}
void testautoptr()
{int a = 10;int* pa = &a;int* pb = pa;*pa = 100;*pb = 200;//两个对象能同时操作同一份资源heqing::auto_ptr<int> ap1(new int);heqing::auto_ptr<int> ap2(ap1);//资源转移带来的缺陷。两个对象不能同时操作同一份资源*ap2 = 200;*ap1 = 100;heqing::auto_ptr<int> ap3;ap3 = ap2;
}
int main()
{testautoptr();return 0;
}

由于上一个实现是通过资源的转移来进行浅拷贝问题,但这样也导致了两个指针不能同时操作一个资源的问题。所以为了解决这个问题,给出以下解决方案。可以个两个指针操作同一份资源的权利,但释放的权利只能有一个指针拥有。

namespace heqing
{//auto_ptr解决的原理就是资源的转移template<class T>class auto_ptr{public://RAIIauto_ptr(T* ptr = nullptr):_ptr(ptr), _owner(false){if (_ptr){_owner = true;}}~auto_ptr(){if (_ptr&&_owner){delete _ptr;_ptr = nullptr;}}//指针特性T& operator*(){return *_ptr;}T* operator->(){return _ptr;}auto_ptr(auto_ptr<T>& ap):_ptr(ap._ptr),_owner(ap._owner){ap._owner = false;}auto_ptr<T>& operator=(auto_ptr<T> ap){if (this != &ap){if (_ptr&&_owner)//如果当前对象管理资源了,先释放原有的资源{delete _ptr;}_ptr = ap._ptr;//资源转移_owner = ap._owner;ap._owner = false;//ap将释放权利转移给了this}return *this;}private:T* _ptr;bool _owner;};
}
void testautoptr()
{heqing::auto_ptr<int> ap1(new int);heqing::auto_ptr<int> ap2(ap1);heqing::auto_ptr<int> ap3;ap3 = ap2;
}
void testautoptr2()
{heqing::auto_ptr<int> ap1(new int);if (true){heqing::auto_ptr<int> ap2(ap1);*ap2 = 20;//出了函数作用域,ap2释放资源}*ap1 = 10;//ap1并不知道,所以会导致野指针
}
int main()
{testautoptr();return 0;
}

上面实现的这个版本的本质是通过管理权限的转移来解决浅拷贝的问题。有可能带来一个比较大的问题:可能会导致野指针。这样的设计在原本就有问题。例子如上。因此,C++98最终使用的还是资源转移来进行实现的。但还是建议在什么情况下都不要使用auto_ptr。

unique_ptr(C++11)
浅拷贝引起的原因是因为默认拷贝构造函数和默认赋值运算符重载。
而unique_ptr提出的解决浅拷贝的方法就是资源独占。不让使用默认的拷贝构造函数和默认的赋值运算符重载。(缺点:应用场景受限)

解决方法:
1,把拷贝构造函数和赋值运算符重载的操作只进行声明,不实现。并将其设为私有。防止用户自己实现。
2,使用=delete将默认的这两个函数删除。

简单的实现unique_ptr:

namespace heqing
{//解决浅拷贝的方式就是资源独占(只能一个对象使用,不能共享),就是禁止调用拷贝构造和赋值运算符重载template < class T >class unique_ptr{public:unique_ptr(T* ptr = nullptr):_ptr(ptr){}~unique_ptr(){if (_ptr){delete _ptr;//缺点:只能处理new出来的资源,不能处理任意类型的资源_ptr = nullptr;}}T& operator*(){retirn *_pre;}T* operator->(){return _ptr;}//解决浅拷贝//C++98的解决方法//private://防止用户在外部实现//    unique_ptr(const unique_ptr<T>& up);//    unique_ptr<T>& operator=(const unique_ptr<T> &up);//C++11的解决方法unique_ptr(const unique_ptr<T>& up) = delete;//删除该默认成员函数unique_ptr<T>& operator=(const unique_ptr<T> &up) = delete;private:T* _ptr;};
}
void TestUniqueptr()
{heqing::unique_ptr<int> up1(new int);//heqing::unique_ptr<int> up2(up1);heqing::unique_ptr<int> up3 (new int);up3 = up1;
}
int main()
{TestUniqueptr();return 0;
}

shared_ptr
shared_ptr是通过采用引用计数的方式解决浅拷贝问题
优势:多个对象之间共享内存
劣势:可能存在循环引用,最终造成资源泄漏
模拟实现shared_ptr

namespace heqing
{template<class T>class shared_ptr{public:shared_ptr(T* ptr = nullptr):_ptr(ptr), _pcount(nullptr){if (_ptr){_pcount = new int(1);}}~shared_ptr(){if (_ptr && 0 == --*_pcount){delete _ptr;delete _pcount;}}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}shared_ptr(const shared_ptr<T>& sp):_ptr(sp._ptr), _pcount(sp._pcount){if (_ptr){++*_pcount;}}shared_ptr<T>& operator=(const shared_ptr<T>& sp){if (this != &sp){//1,与就资源断开联系,保证当前资源只有this一个对象在控制,释放该资源if (_ptr && 0 == --*_pcount){delete _ptr;delete _pcount;}//2.与sp共享资源和计数_ptr = sp._ptr;_pcount = sp._pcount;if (_ptr){++*_pcount;}}return *this;}int use_count(){return *_pcount;}private:T* _ptr;int* _pcount;};
}
void Testsharedptr()
{heqing::shared_ptr<int> sp1(new int);cout << sp1.use_count() << endl;heqing::shared_ptr<int> sp2(sp1);cout << sp1.use_count() << endl;cout << sp2.use_count() << endl;heqing::shared_ptr<int> sp3(new int);cout << sp3.use_count() << endl;}
int main()
{Testsharedptr();system("pause");return 0;
}

模拟实现的shared_ptr有一个最大的安全隐患,它是线程不安全的。因此作出了以下的改进

#include<mutex>
template<class T>
class DFDef
{
public:void operator()(T*& ptr){if (ptr){delete ptr;ptr = nullptr;}}
};namespace heqing
{template<class T,class DF=DFDef<T>>class shared_ptr{public:shared_ptr(T* ptr = nullptr):_ptr(ptr), _pcount(nullptr), _pMutex(nullptr){if (_ptr){_pcount = new int(1);_pMutex = new mutex;}}~shared_ptr(){Release();}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}shared_ptr(const shared_ptr<T>& sp):_ptr(sp._ptr), _pcount(sp._pcount), _pMutex(sp._pMutex){AddRef();}shared_ptr<T>& operator=(const shared_ptr<T>& sp){if (this != &sp){Release();_ptr = sp._ptr;_pcount = sp._pcount;if (_ptr){AddRef();}}return *this;}int use_count(){return *_pcount;}private:void AddRef(){if (_pcount){_pMutex->lock();++*_pcount;_pMutex->unlock();}}int SubRef(){if (_pcount){_pMutex->lock();--*_pcount;_pMutex->unlock();}return *_pcount;}void Release(){if (_ptr && 0 == SubRef()){DF()(_ptr);delete _pcount;}}T* _ptr;int* _pcount;mutex* _pMutex;};
}

shared_ptr可能造成循环引用的问题
先看下面这段代码

#include <memory>
struct ListNode
{ListNode(int data = 0): pre(nullptr), next(nullptr), _data(data){cout << "ListNode(int):" << this << endl;}~ListNode(){cout << "~ListNode():" << this << endl;}shared_ptr<ListNode> pre;shared_ptr<ListNode> next;int _data;
};void TestListNode()
{shared_ptr<ListNode> sp1(new ListNode(10));shared_ptr<ListNode> sp2(new ListNode(20));sp1->next = sp2;sp2->pre = sp1;
}int main()
{TestListNode();return 0;
}


从上图中可以看到无限循环的问题。当我们想要删除其中的某一个节点时,就会造成无限循环的问题。例如我们要删除sp1这个节点,我们需要把_pcount的值置为1才能进行删除,那我们就需要删除sp2->pre这个节点,而我们要删除这个节点就需要销毁sp2。如果要销毁sp2,就需要把sp2的_pcount置为1,那就需要删除sp1的next。这样一来就陷入了无限循环中。这样就导致两个对象的引用计数_pcount都无法达到0,最终都无法释放堆上的资源,从而导致内存泄漏。

解决循环引用的问题
使用weak_ptr来解决shared_ptr中存在的循环引用的问题。weak_ptr的实现原理和shared_ptr类似,都是通过引用计数的方式。weak_ptr不能独立的管理资源,因为weak_ptr必须配合shared_ptr。

struct ListNode
{ListNode(int data = 0): _data(data){cout << "ListNode(int):" << this << endl;}~ListNode(){cout << "~ListNode():" << this << endl;}weak_ptr<ListNode> pre;weak_ptr<ListNode> next;int _data;
};void TestListNode()
{shared_ptr<ListNode> sp1(new ListNode(10));shared_ptr<ListNode> sp2(new ListNode(20));sp1->next = sp2;sp2->pre = sp1;
}int main()
{// weak_ptr<int> sp1;  // 可以编译成功//weak_ptr<int> sp2(new int);   // 编译失败--原因:weak_ptr不能独立管理资源TestListNode();return 0;
}

智能指针的应用场景
1,在任何情况下都尽量不要使用auto_ptr
2,如果不需要对象资源共享------unique_ptr
3,需要对象资源共享---------------shared_ptr
4,出现循环引用是采用weak_ptr

C++ -- 智能指针 auto_ptr,unique_ptr,shared_ptr的简单实现和原理相关推荐

  1. C++——智能指针——auto_ptr、shared_ptr、unique_ptr

    1.4.智能指针 智能指针是行为类似于指针的类对象. C++11 中提供了三种智能指针,使用这些智能指针时需要引用头文件 : ·shared_ptr; ·unique_ptr; ·auto_ptr; ...

  2. 模拟实现智能指针auto_ptr,scoped_ptr,shared_ptr

    智能指针,顾名思义它是一个聪明的指针,那么到底聪明到哪了呢,让我们一起来看以下的代码. void test1() //内存泄露 {int *p = new int(1);if (1){//...ret ...

  3. 智能指针的用法shared_ptr

    目录 智能指针 shared_ptr共享的智能指针 shared_ptr的基本用法 1.初始化 reset成员函数 make_shared swap()函数 2. 获取原始指针(get()) 3. 指 ...

  4. C++的智能指针auto_ptr、unique_ptr源码解析

    C++的智能指针auto_ptr.unique_ptr源码解析 1.前言 2.源码准备 3.源码解析 3.1.auto_ptr解析 3.2.unique_ptr解 3.3.unique_ptr的一个偏 ...

  5. C++11智能指针(unique_ptr、shared_ptr、weak_ptr)boost::scoped_ptr

    C++11智能指针(unique_ptr.shared_ptr.weak_ptr)_-码农小非-的专栏-CSDN博客_c++ shared_ptr weak_ptr 原创)智能指针拾遗 (原创)智能指 ...

  6. c++系列 —— 智能指针auto_ptr和unique_ptr

    往期地址: c++系列一 -- c++的封装 c++系列二 -- c++的继承 c++系列三 -- 继承和多态特性 c++系列四 -- 运算符重载 c++系列五 -- 静态成员和静态类 c++系列六 ...

  7. 深入学习c++--智能指针(三) unique_ptr

    1. 几种智能指针 1. auto_ptr: c++11中推荐不使用他(放弃) 2. shared_ptr: 拥有共享对象所有权语义的智能指针 3. unique_ptr: 拥有独有对象所有权语义的智 ...

  8. 智能指针之unique_ptr

    unique_ptr实现的是专属所有权语义,用于独占它所指向的资源对象的场合.某个时刻只能有一个unique_ptr指向一个动态分配的资源对象,也就是这个资源不会被多个unique_ptr对象同时占有 ...

  9. C++自学24:唯一智能指针(make_unique/unique_ptr/reset/release/get/13.1)

    使用make_unique获取一个智能指针,智能指针的类型是unique_ptr // a不是数组,小括号里的就是值 std::unique_ptr<int> a = std::make_ ...

最新文章

  1. 一个基于SpringBoot + Mybatis + Vue的代码生成器
  2. 一个有趣的小例子,带你入门协程模块-asyncio
  3. 处理2D图像和纹理——显示文字
  4. 复制一个5G文件只需要两秒,全网最牛方法!
  5. asp.net将内容导出到Excel,Table表格数据(html)导出EXCEL
  6. 中国股市暴涨暴跌全记录
  7. 开源 | 近期遥感航空影像的检索、检测、跟踪、计数、分类相关工作
  8. 三年磨一剑,钉的真好听 | 凌云时刻
  9. ANSI SQL 定义
  10. 开启TOGAF架构之路
  11. ae效果英文版翻译对照表_AE特效菜单中英文对照
  12. php json转数组示例,php json转数组的例子
  13. Hexo 设置博客背景图片 (NexT 主题 )
  14. 你装陈桥(五笔)啦吗,这是什么...!(qcssb19.exe)
  15. SQL数据库移植到ARM板步骤
  16. flea-cache使用之Memcached接入
  17. 核酸检测软件开发方案
  18. 加边的无向图(并查集)
  19. [BBS 水木清华站]给Linux新手
  20. 程序设计与算法(一)第7周测验(2019夏季)

热门文章

  1. java 文本编辑器_「java文本编辑器」用Java实现文本编辑器 - seo实验室
  2. 微信自定义分享(php方法)
  3. 因AlphaFold获300万美元“科学界奥斯卡奖”,DeepMind创始人:震惊,捐了
  4. 关于小米手机锁屏功能是否已经开启的提问
  5. 贵州大数据产业路线图出炉 7年内将达4500亿元
  6. 港联证券|东财暴拉14%,更有牛股13天翻倍!牛市旗手全线沸腾
  7. 关于短路保护三极管对管应用电路
  8. js正则表达式获得url参数
  9. Windows中汉字输入方式的启动和汉字输入方法
  10. 《程序员的呐喊》读书笔记(上)