为什么需要智能指针

在写代码的时候,从堆上申请的空间,由于一些原因(代码中途异常),没有得到释放,从而导致代码出现内存泄漏,所以为了防止此类问题的出现,从而出现智能指针。采用智能指针可以自动检测,指针如果不用,则会自动释放堆上的空间。

智能指针作用
管理指针,自动释放资源。

RAII

RAII是一种利用对象生命周期来控制程序资源的技术

在类中,对象构造的时候获取资源,最后在对象析构后,释放资源,这样就不用担心内存资源没有被释放。如果将申请资源的指针交给类来管理(管理:释放资源),就在该函数结束时,编译器自动调用类的析构函数,完成对类管理资源的释放。实际上就是将指针交给类进行管理,在构造函数中申请资源,在析构函数中,释放资源。

  • 优点
    不用显示释放资源
    对象在使用中资源始终有效


同时RAII,还需要让该类具有像指针一样的方式,只需要对* 与->两个运算符重载。
缺点:RAII存在浅拷贝问题。

template<class T>
class Smartptr
{public:Smartptr(T *ptr = nullptr):_ptr(ptr){}~Smartptr(){if(_ptr)delete _ptr;}
private:T* _ptr;
};void testSmartptr()
{int *p = new int;Smartptr<int> sp(p);
}

智能指针的原理

上面讲的RAII的实现方法,还不能算智能指针,因为其少了指针的几个特有的操作

  1. 解引用*
  2. 指向操作 ->

因此只需要将上面的两种操作在RAII类中进行重载就可以。

template<class T>class Smartptr
{public:Smartptr(T *ptr = nullptr):_ptr(ptr){}~Smartptr(){if (_ptr)delete _ptr;}T& operator*() //指针所指向空间里面的内容{return *_ptr;}T* operator->()//返回的是指针的地址{return _ptr;}
private:T* _ptr;
};struct Date
{int a;int b;
};
void testSmartptr()
{Smartptr<int> sp(new int);  //sp相当于一个对象,用来管理资源*sp = 10;cout << *sp << endl;Smartptr<Date> sp2(new Date);sp2->a = 1;sp2->b = 2;cout << sp2->a << sp2->b << endl;
}

上面这种方法在拷贝构造函数与赋值运算符重载方法中不可行,容易产生浅拷贝。但是不能使用深拷贝来解决,因为深拷贝会产生两块空间,同时,资源是外部用户提供的,智能指针没有申请空间的权限,只能对资源进行管理。

  • C++98 中 autoPtr原理

在autoPtr库函数中,实现原理就是在拷贝构造函数与赋值运算符重载中,将后面对象的内容转移到前面对象里面去,最后将后面的对象内容置空。

template<class T>class AutoPtr{public:AutoPtr(T* ptr):_ptr(ptr){}~AutoPtr(){if (_ptr)delete _ptr;}AutoPtr(AutoPtr<T>&sp) //拷贝构造函数:_ptr(sp._ptr){sp._ptr = nullptr;}AutoPtr<T>& operator=(AutoPtr<T>&sp){if (*this != sp)  //先判断是否自己给自己赋值{if (_ptr)  //如果赋值等号前面的有空间,需将其释放delete _ptr;_ptr = sp._ptr;sp._ptr = nullptr;}return *this;}AutoPtr* operator->(){return this;}AutoPtr& operator*(){return *this;}private:T* _ptr;};

因为发生深拷贝与赋值时,只能使用一份,所以c++标准委员会不建议使用。

auto_ptr的改进版本:
实现原理:增加一个参数,bool owner,用来管理资源释放的权利。

if (_ptr && _owner) //资源存在,同时拥有资源释放的权利delete _ptr;
template<class T>
class auto_ptr
{public:auto_ptr(T* ptr = nullptr):_ptr(ptr), _owner(false){    if (_ptr)_owner = true;}auto_ptr( auto_ptr<T>& p):_ptr(p._ptr),_owner(p._owner){p._owner = false;}auto_ptr& operator*(){return *_ptr;}auto_ptr* operator->(){return _ptr;}auto_ptr<T>& operator = (auto_ptr& p){if (this != &p){if (_ptr && _owner)delete _ptr;_ptr = p._ptr;_owner = p._owner;p._owner = false;}return *this;}~auto_ptr(){if (_ptr && _owner)delete _ptr;}
private: T* _ptr;bool _owner;
};

auto_ptr的改性版本可能会造成野指针。

  • unique_ptr库函数,因为智能指针容易产生浅拷贝,所以就禁止拷贝构造函数与赋值运算符的重载,将拷贝构造函数和赋值运算符重载写成私有成员函数。
template<class T>
class  Uniqueptr
{public:Uniqueptr(T* ptr = nullptr):_ptr(ptr){}~Uniqueptr(){if (_ptr)delete _ptr;[]}Uniqueptr* operator->(){return this;}Uniqueptr& operator*(){return *this;}Uniqueptr(Uniqueptr<T>const &) = delete;Uniqueptr<T>& operator = (Uniqueptr<T>const &) = delete;
private:T* _ptr;
};
  • shared_ptr
    就是通过计数方式实现资源的共享。通过计数的方式来判断是否需要进行释放资源,shared_ptr,给每份资源维护了一个计数,用来记录每份资源被几个对象共享,在对象调用析构函数时,计数-1,当计数减到0时,表示最后一份资源不适用了,最后释放该资源。当计数不是0时表示该资源还在使用中,不能释放该资源。

shared_ptr = RAII + operator* +opeartor-> + 计数

#include<mutex> //并发程序互斥锁
template<class T>
class shared_ptr
{public:shared_ptr(T *ptr):_ptr(ptr),_pcount(new int(1))//,_pmutex(new mutex){}~shared_ptr(){Release();}shared_ptr(const shared_ptr<T> &sp):_ptr(sp._ptr),_pcount(sp._pcount)//,_pmutex(sp._pmutex){addcount();}shared_ptr<T>& operator=(const shared_ptr<T> &sp){if (_ptr != sp._ptr){Release(); //释放旧空间_ptr = sp._ptr;_pcount = sp._pcount;addcount();           //计数+1//_pmutex = sp._pmutex;}return *this;}T& operator*(){return *this;}T* operator->(){return this;}void addcount(){//_pmutex->lock(); //加锁++(*_pcount);            //计数+1//_pmutex->unlock();//解锁}void Release(){//bool deleteflag = false;if (0 == --(*_pcount)){delete _ptr;delete _pcount;}/*if (deleteflag == ture){delete _pmutex;}*/}int usecount(){return *_pcount;}
private:T * _ptr;int *_pcount;//mutex* _pmutex; 多线程加锁
};

shared_ptr改进,由于shared_ptr,所管理的指针有多重申请方式,导致不能将释放函数写成固定的,所以析构函数需要模板特化。

//定制删除器
template<class T>
class DFDel //默认new出来的空间
{public:void operator()(T*& p){if (p){delete p;p = nullptr;}}
};template<class T>
class Free //malloc申请的空间
{public:void operator()(T*& p){if (p){free(p);p = nullptr;}}
};class Fclose
{public:void operator()(FILE* p){if (p){fclose(p);p = nullptr;}}
};namespace bai
{template<class T,class DF = DFDel<T>>  //DF为删除类型,DF默认为new出来的空间class shared_ptr{public:shared_ptr(T* ptr = nullptr):_ptr(ptr){if (_ptr)_pcount = new int(1);}shared_ptr(const shared_ptr<T>& p):_ptr(p._ptr), _pcount(p._pcount){if (_ptr) // 如果资源不为nullptr++(*_pcount);}//p1 == p2//p1:未管理资源------直接p2共享//p1:单独管理资源----在于p2共享之前先释放自己的资源//p1:与其他对象共享资源---p1计数--,p2计数++shared_ptr<T> operator = (const shared_ptr<T>& p){if (this != &p){if (_ptr && 0 == --(*_pcount)){delete _ptr;delete _pcount;}_ptr = p._ptr;_pcount = p._pcount;if (_ptr)++(*_pcount);}return *this;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}~shared_ptr(){if (_ptr && --(*_pcount) == 0) //由于资源的申请方式不同,{                            //所以需要根据资源的类型,定制释放的方式//delete _ptr;DF()(_ptr);               //DF相当于一种类型,DF()相当于创建一个无名的对象,DF()(_ptr),相当于DF对象调用delete _pcount;}}int usecount(){return *_pcount;}private:T* _ptr;int* _pcount;};
}
void TestFunc()
{bai::shared_ptr<int,DFDel<int>> p1(new int);bai::shared_ptr<int,DFDel<int>> p2(p1);bai::shared_ptr<FILE, Fclose> p3(fopen("666.text" ,"rb"));
}

shared_ptr的缺陷:
当使用智能指针管理双向链表时,容易产生循环引用。

struct ListNode
{ListNode(int data = int()):_pre(nullptr), _next(nullptr){cout << "ListNode()" << this << endl;}~ListNode(){cout << "~ListNode()" << endl;}shared_ptr<ListNode> _pre;shared_ptr<ListNode> _next;int data;
};void TestFunc()
{shared_ptr<ListNode> p1(new ListNode(10));shared_ptr<ListNode> p2(new ListNode(20));cout << p1.use_count() << endl;//查看p1中的引用计数cout << p2.use_count() << endl;p1->_next = p2;p2->_pre = p1;cout << p1.use_count() << endl;cout << p2.use_count() << endl;
}


没有调用析构函数,导致资源泄露
解决shared_ptr循环引用
weak_ptr:不能单独管理资源,必须配合shared_ptr一起使用。
weak_ptr:就是为了解决shared_ptr存在的循环引用

struct ListNode
{ListNode(int _data = int()):data(_data){cout << "ListNode()" << this << endl;}~ListNode(){cout << "~ListNode()" << endl;}weak_ptr<ListNode> _pre;  //****weak_ptr<ListNode> _next;int data;
};

因为weak_ptr不能单独管理资源,所以构造函数不能给_pre,_next初始化

C++---智能指针原理讲解相关推荐

  1. c++智能指针 示例讲解

    智能指针实际上就是通过模板技术实现的一个类 内存泄露(臭名昭著的Bug)--在软件开发和测试阶段都不容易被发现 -动态申请堆空间,用完后不归还 -C++语言中没有垃圾回收的机制 -指针无法控制所指堆空 ...

  2. 智能指针实战讲解--龙之介《Effective C++》实验室

    条款13 以对象管理资源 在C++中 我们申请资源(new) 之后需要手动释放资源(delete) Test* t = new Test();//申请资源......delete t;//释放资源 但 ...

  3. C++RAII机制(智能指针原理)

    原文详细整理 C++中的RAII机制 - 码到城攻RAII机制,是C++语言的一种管理资源.避免泄漏的惯用法https://www.codecomeon.com/posts/200/ 何为RAII R ...

  4. C++智指针之——boost::intrusive_ptr,一种与shared_ptr、unique_ptr截然不同的智能指针

    智能指针boost::shared_ptr/std::shared_ptr使用了和数据无关的引用计数,在使用shared_ptr之前,必须要搞清楚资源的所有权和资源的使用权这两个问题(详见<C+ ...

  5. 【C++ 语言】智能指针 引入 ( 内存泄漏 | 智能指针简介 | 简单示例 )

    文章目录 I . 智能指针 引入 II . 智能指针 简介 III . 智能指针 简单示例 I . 智能指针 引入 1 . 示例前提 : 定义一个 Student 类 , 之后将该类对象作为智能指针指 ...

  6. 窥见C++11智能指针

    导语: C++指针的内存管理相信是大部分C++入门程序员的梦魇,受到Boost的启发,C++11标准推出了智能指针,让我们从指针的内存管理中释放出来,几乎消灭所有new和delete.既然智能指针如此 ...

  7. 智能指针:-和*运算符重载 + 模板技术 实现智能指针(C++)

    智能指针介绍 在C++中,我们都知道在堆区new 开辟的内存,必须通过delete 进行内存释放,不然会形成内存泄漏.有时候我们使用了new 后在 写了很多代码,忘记delete 也是很正常的.那么我 ...

  8. qt 如何 指针 自动 释放内存_要是面试官再问你智能指针的问题,就拿这篇文章“盘他”!!!...

    前一段时间,有不少朋友问我关于智能指针的问题,并且反映经常会在面试中被面试官问到,所以今天小豆君就来讲讲我对智能指针的理解,希望能对大家有所帮助 既然讲智能指针,我们就先来看看它为什么会出现. 1 传 ...

  9. 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 原创)智能指针拾遗 (原创)智能指 ...

最新文章

  1. java list 去重 相同的相加_Java 中的数据流和函数式编程 | Linux 中国
  2. SAP MM 采购申请中的物料组字段改成Optional
  3. centos5.5中安装mysql5.5.3
  4. .NET 开源项目 StreamJsonRpc 介绍[下篇]
  5. stage3d 骨骼优化
  6. Linux系统编程----12(线程概念,Linux线程实现原理,栈中ebp指针和ebp指针,线程的优缺点和共享资源)
  7. openstack服务编排
  8. Maven之自定义pom类型的基础项目
  9. Silverlight进度条控件动画源代码
  10. 从零开始造一个“智障”聊天机器人
  11. phpstudy运行PHP项目出现404怎么办?
  12. 超简单的内网邮件服务器搭建(CentOS7 postfix+dovecot)
  13. JAVA 注解示例 详解
  14. python课程典范选优_python 实现选课系统
  15. MapReduce输出结果到多个文件
  16. Android 开发笔记___图像按钮__imageButton
  17. P9813驱动RGB灯珠
  18. MySQL8.0密码找回与权限刷新
  19. [资料] 为实现正确信号调理的噪声计算,这七个步骤你得get(转载)
  20. 基于Java+JSP+MySQL基于SSM的医院挂号就诊系统

热门文章

  1. hihoCoder1687(向量叉积)
  2. 移动开发--移动web特别样式处理
  3. 基于jquery的tab切换
  4. spring事务管理的一些注意点
  5. 字节流通向字符流的桥梁:InputStreamReader
  6. Android 即时通讯开发
  7. ado.net Oracle中一次执行多条sql语句
  8. 机器学习实现线性梯度算实现octave
  9. Java实现的基于socket的一次通信
  10. Extjs4 Tab面板Mapbar地图关闭再打开气泡失效问题解决