码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 【c++智能指针】


    目录

    • 一、智能指针的使用及原理
    • 二、auto_ptr
    • 三、unique_ptr
    • 三、shared_ptr
    • 四、weak_ptr
    • 五、定制删除器

    一、智能指针的使用及原理

    RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:
    不需要显式地释放资源。
    采用这种方式,对象所需的资源在其生命期内始终保持有效。
    比如:为什么智能指针前:

    #include"smartptr.h"
    class A
    {
    public:
    	A(int a)
    		:_a(a)
    	{
    		//cout << "A(int a)" << endl;
    	}
    
    	~A()
    	{
    		cout << "~A()" << endl;
    	}
    
    private:
    	int _a;
    };
    int div()
    {
    	int a, b;
    	cin >> a >> b;
    	if (b == 0)
    		throw invalid_argument("除0错误");
    	return a / b;
    }
    void Func()
    {
    	//Ting::SmartPtr sp1(new A(10));
    	//Ting::SmartPtr sp2(new A(20));
    	A* a = new A(10);
    	cout << div() << endl;
    }
    int main()
    {
    	try {
    		Func();
    	}
    	catch (const exception& e)
    	{
    		cout << e.what() << endl;
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    在这里插入图片描述
    使用后:

    #include
    using namespace std;
    
    namespace Ting
    {
    	template<class T>
    	class SmartPtr
    	{
    	public:
    		SmartPtr(const T* ptr)
    			:_ptr(ptr)
    		{}
    
    		T& operator*()
    		{
    			return *_ptr;
    		}
    
    		T* operator->()
    		{
    			return _ptr;
    		}
    
    		~SmartPtr()
    		{
    			if (_ptr)
    			{
    				delete _ptr;
    			}
    		}
    
    	private:
    		const T* _ptr;
    	};
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    #include"smartptr.h"
    class A
    {
    public:
    	A(int a)
    		:_a(a)
    	{
    		//cout << "A(int a)" << endl;
    	}
    
    	~A()
    	{
    		cout << "~A()" << endl;
    	}
    
    private:
    	int _a;
    };
    int div()
    {
    	int a, b;
    	cin >> a >> b;
    	if (b == 0)
    		throw invalid_argument("除0错误");
    	return a / b;
    }
    void Func()
    {
    	Ting::SmartPtr<A> sp1(new A(10));
    	Ting::SmartPtr<A> sp2(new A(20));
    	//A* a = new A(10);
    	cout << div() << endl;
    }
    int main()
    {
    	try {
    		Func();
    	}
    	catch (const exception& e)
    	{
    		cout << e.what() << endl;
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    在这里插入图片描述

    二、auto_ptr

    C++98版本的库中就提供了auto_ptr的智能指针。下面演示的auto_ptr的使用及问题。
    在这里插入图片描述
    模拟实现auto_ptr:

    template<class T>
    class auto_ptr
    {
    public:
    	auto_ptr (T* ptr)
    		:_ptr(ptr)
    	{}
    
    	auto_ptr(auto_ptr<T>& ap)
    	{
    		_ptr = ap._ptr;
    		//悬空
    		ap._ptr = nullptr;
    
    	}
    
    	auto_ptr<T>& operator=(auto_ptr<T>& ap)
    	{
    		if (_ptr != ap._ptr)
    		{
    			if (_ptr)
    			{
    				delete _ptr;
    			}
    			_ptr = ap._ptr;
    			//悬空
    			ap._ptr = nullptr;
    		}
    		return *this;
    
    	}
    
    	T& operator*()
    	{
    		return *_ptr;
    	}
    
    	T* operator->()
    	{
    		return _ptr;
    	}
    
    	~auto_ptr()
    	{
    		if (_ptr)
    		{
    			delete _ptr;
    		}
    	}
    
    private:
    	T* _ptr;
    };
     // 结论:auto_ptr是一个失败设计,很多公司明确要求不能使用auto_ptr
     //int main()
     //{
     //  std::auto_ptr sp1(new int);
     //  std::auto_ptr sp2(sp1); // 管理权转移
    //
     //  // sp1悬空
    //  *sp2 = 10;
     //  cout << *sp2 << endl;
     //  cout << *sp1 << endl;
     //  return 0;
     //}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    三、unique_ptr

    为了解决上面auto_ptr拷贝赋值后因为管理权限转移而造成悬空问题,C++11引入了unique_ptr
    unique_ptr的实现原理:简单粗暴的防拷贝,下面简化模拟实现了一份UniquePtr来了解它的原理

    template<class T>
    	class unique_ptr
    	{
    	public:
    		unique_ptr(T* ptr)
    			:_ptr(ptr)
    		{}
    
    		unique_ptr(const unique_ptr<T>& ap) = delete;
    		unique_ptr<T>& operator=(const unique_ptr<T>& ap) = delete;
    
    
    		T& operator*()
    		{
    			return *_ptr;
    		}
    
    		T* operator->()
    		{
    			return _ptr;
    		}
    
    		~unique_ptr()
    		{
    			if (_ptr)
    			{
    				delete _ptr;
    			}
    		}
    
    	private:
    		T* _ptr;
    	};
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    三、shared_ptr

    C++11中开始提供更靠谱的并且支持拷贝的shared_ptr
    shared_ptr的原理:是通过引用计数的方式来实现多个shared_ptr对象之间共享资源。

    1. shared_ptr在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象共享。
    2. 在对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减一。
    3. 如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源;
    4. 如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了。
      下面简化模拟实现了一份shared_ptr来了解它的原理
    template<class T>
    class shared_ptr
    {
    public:
    	shared_ptr(T* ptr)
    		:_ptr(ptr)
    		,_pcount(new int(1))
    	{}
    
    	shared_ptr(shared_ptr<T>& sp)
    		:_ptr(sp._ptr)
    		,_pcount(sp._pcount)
    	{
    		++(*_pcount);
    	}
    
    	shared_ptr<T>& operator=(shared_ptr<T>& sp)
    	{
    		//防止自己给自己赋值
    		if (_ptr != sp._ptr)
    		{
    			if (_ptr)
    			{
    				delete _ptr;
    			}
    			_ptr = sp._ptr;
    			_pcount = sp._pcount;
    
    			++(*_pcount);
    		}
    		return *this;
    	}
    
    	~shared_ptr()
    	{
    		if (--(*_pcount) == 0)
    		{
    			delete _ptr;
    			delete _pcount;
    		}
    	}
    
    	T* operator->()
    	{
    		return _ptr;
    	}
    
    	T& operator*()
    	{
    		return *_ptr;
    	}
    private:
    	T* _ptr;
    	int* _pcount;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    但是shared_ptr再有些场景下会出现循环引用问题。如:
    在这里插入图片描述
    没有调用Node的析构函数。为什么呢?如图:
    在这里插入图片描述

    四、weak_ptr

    为了解决shared_ptr循环引用场景,所以又引入了weak_ptr。
    在这里插入图片描述
    下面简化模拟实现了一份weak_ptr来了解它的原理

    template<class T>
    class weak_ptr
    {
    public:
    	weak_ptr()
    		:_ptr(nullptr)
    	{}
    
    	weak_ptr(Ting::shared_ptr<T>& sp)
    		:_ptr(sp.get())
    	{}
    
    	weak_ptr<T>& operator=(Ting::SmartPtr<T>& sp)
    	{
    		_ptr = sp.get();
    		return *this;
    	}
    
    	T* operator->()
    	{
    		return _ptr;
    	}
    
    	T& operator*()
    	{
    		return *_ptr;
    	}
    
    private:
    	T* _ptr;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    五、定制删除器

    上面模拟的shared_ptr只能在少部分场景下使用,在这个场景下使用不了。如:
    在这里插入图片描述
    所以我们可以模拟库里在构造时加个仿函数过去。
    在这里插入图片描述
    属性里也加个,用包装器来申明。
    在这里插入图片描述
    析构函数改成调用仿函数。
    在这里插入图片描述
    完整代码:

    template<class T>
    class shared_ptr
    {
    public:
    	shared_ptr(T* ptr)
    		:_ptr(ptr)
    		, _pcount(new int(1))
    	{}
    
    	template<class D>
    	shared_ptr(T* ptr,D del)
    		:_ptr(ptr)
    		,_pcount(new int(1))
    		,_del(del)
    	{}
    
    	shared_ptr(shared_ptr<T>& sp)
    		:_ptr(sp._ptr)
    		,_pcount(sp._pcount)
    	{
    		++(*_pcount);
    	}
    
    	shared_ptr<T>& operator=(shared_ptr<T>& sp)
    	{
    		//防止自己给自己赋值
    		if (_ptr != sp._ptr)
    		{
    			if (_ptr)
    			{
    				delete _ptr;
    			}
    			_ptr = sp._ptr;
    			_pcount = sp._pcount;
    
    			++(*_pcount);
    		}
    		return *this;
    	}
    
    	~shared_ptr()
    	{
    		if (--(*_pcount) == 0)
    		{
    
    			_del(_ptr);
    			delete _pcount;
    		}
    	}
    
    	T* get()
    	{
    		return _ptr;
    	}
    
    	T* operator->()
    	{
    		return _ptr;
    	}
    
    	T& operator*()
    	{
    		return *_ptr;
    	}
    private:
    	T* _ptr;
    	int* _pcount;
    	function<void(T*)> _del = [](T* ptr) {delete ptr; };
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
  • 相关阅读:
    FFmpeg修复受损视频
    推荐算法---矩阵分解
    FFplay文档解读-47-多媒体过滤器一
    Java中 “% “与 “/“有什么不同之处呢?
    为什么学了模数电还是看不懂较复杂的电路图?
    fastadmin 后台列表数据多表查询筛选
    【gitlab】我的gitlab经历--20220901
    【Python系列】数字的bool值
    pandas 统计函数
    【Python】对MySQL数据库的操作(SQLAlchemy)
  • 原文地址:https://blog.csdn.net/qq_69544758/article/details/133856861
  • 最新文章
  • 【FHE】我们如何实现同态加密推理(八):SiLU 的密文化——两条路径,和一个 8 字节的开关
    SQLcl 中 c/old/new 替换变量为何失效?先定位再替换
    不让模型写作文,直接从它脑子里读答案:Jev 决策在 .NET 的两条路线
    Halo插件:支持GitHub的图床附件策略
    电视投屏界的“一股清流”!SimpleTVCast 6.0.0-beta 抢先体验:无需TV端App,界面大换血!✨
    华三VXLAN集中式网关实战指南:Spine多活网关 + 双活接入(M-LAG)
    软件更好写之后口碑会更重要
    大 O、大 Omega、大 Theta 的区别,O(n) 复杂度是什么意思
    Java标签跳转:一个鲜为人知但却很实用的小技巧
    多智能体系统论文速读
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
小工具 小游戏
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1

京公网安备 11010502049817号