• C++智能指针原理与实现


    智能指针

    智能指针基于RAII技术(Resource Acquisition Is Initialization,即资源获取即初始化,构造时分配资源,析构时释放资源)。

    用指针构造智能指针对象,并在对象析构时自动释放指针指向的内存空间。

    在智能指针对象中,重载了 * 和 -> 两个操作符,使其具有指针一样的行为。

    C++的智能指针有以下四种:

    1、auto_ptr

    • 对于拷贝构造,auto_ptr会对拷贝对象的指针进行置空处理,这样就导致拷贝对象不能再被使用;
    • 而对于赋值重载,auto_ptr会对被赋值对象的资源进行释放,复制对象的指针也被置空,最终只剩下被复制对象这一份指针。
    template
    class auto_ptr
    {
    public:
    	auto_ptr(T* ptr)
    		: _ptr(ptr)
    	{}
    	auto_ptr(auto_ptr& ap)
    	{
    		_ptr = ap._ptr;
    		ap._ptr = nullptr;
    	}
    	auto_ptr& operator=(auto_ptr& ap)
    	{
    		if (this != &ap)
    		{
    			delete _ptr;
    			_ptr = ap._ptr;
    			ap._ptr = nullptr;
    		}
    		return *this;
    	}
    	T& operator*()
    	{
    		return *_ptr;
    	}
    	T* operator->()
    	{
    		return _ptr;
    	}
        ~auto_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
    • 34
    • 35
    • 36
    • 37

    2、unique_ptr

    unique_ptr在auto_ptr的基础上直接禁用了拷贝构造和赋值重载(delete),从而避免了auto_ptr在使用上容易出错的问题,但是没有了这两个函数,对于用户非常不便。

    3、shared_ptr

    基于写时拷贝技术,通过计数器的加减判断是否需要释放指针指向的空间。

    // shared_ptr模拟实现
    template 
    class shared_ptr
    {
    public:
    	shared_ptr(T* ptr)
    		: _ptr(ptr)
    		, _pCount(new int(1))
    	{}
    
    	shared_ptr(const shared_ptr& sp)
    	{
    		_ptr = sp._ptr;
    		_pCount = sp._pCount;
    		++* _pCount;
    	}
    
    	shared_ptr& operator=(const shared_ptr& sp)
    	{
    		if (_ptr != sp._ptr)
    		{
    			if (--(*_pCount) == 0)
    			{
    				delete _ptr;
    				delete _pCount;
    			}
    
    			_ptr = sp._ptr;
    			_pCount = sp._pCount;
    			++* _pCount;
    		}
    		return *this;
    	}
    
    	~shared_ptr()
    	{
    		if (--(*_pCount) == 0 && _ptr != nullptr)
    		{
    			delete _ptr;
    			delete _pCount;
    		}
    	}
    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

    shared_ptr的问题

    1. 计数器是线程安全的,但是利用指针读写依然需要互斥量的参与
    2. shared_ptr在循环引用(比如双链表)时会出现资源泄露的问题。解决办法:weak_ptr
    3. C++库中的智能指针默认使用delete进行指针指向空间的释放,但是如果指针是new []或者malloc出来的,那么使用delete就会出现问题。解决办法:在构造智能指针时可以提供删除器Deleter(仿函数),那么析构函数会自动调用该删除器进行资源回收。

    注:weak_ptr拷贝shared_ptr时,不会导致share_ptr的计数器增加,从而解决循环引用的问题。

    4、weak_ptr

    引用计数有一个问题就是互相引用形成环(环形引用),这样两个指针指向的内存都无法释放。需要使用weak_ptr打破环形引用。

    weak_ptr是一个弱引用,它是为了配合shared_ptr而引入的一种智能指针,它指向一个由shared_ptr管理的对象而不影响所指对象的生命周期,也就是说,它只引用,不计数

    如果一块内存被shared_ptr和weak_ptr同时引用,当所有shared_ptr析构了之后,不管还有没有weak_ptr引用该内存,内存也会被释放。所以weak_ptr不保证它指向的内存一定是有效的,在使用之前使用函数lock()检查weak_ptr是否为空指针。

    在底层实现中,每个 std::shared_ptrstd::weak_ptr 都有一个关联的控制块,这个控制块包含两个计数器:一个是共享所有权的强引用计数器(由 std::shared_ptr 持有),另一个是弱引用计数器(由 std::weak_ptr 持有)。

    弱引用计数器可以方便weak_ptr知道自己指向的shared_ptr是否已被销毁。

    当最后一个 std::shared_ptr 被销毁时,它会删除所指向的对象,但如果还有 std::weak_ptr 指向该对象,则控制块将保持不变。当最后一个指向对象的 std::weak_ptr 被销毁时,它将清理控制块。

  • 相关阅读:
    基于51单片机步进电机加减速正反转数码管显示( proteus仿真+程序+原理图+设计报告+讲解视频)
    分位数回归的求解
    【IC刷卡数据专题】IC刷卡数据分析的技术要点
    一些常用的兼容性测试方法和技巧
    Shell 脚本面试指南
    Go Web——简单blog项目(三)
    使用scp在多个linux系统间进行文件拷贝
    记录一次阿里云ECS服务器系统盘扩容
    普元EOS学习笔记-EOS8.3精简版安装
    【力扣-每日一题】714. 买卖股票的最佳时机含手续费
  • 原文地址:https://blog.csdn.net/Wyf_Fj/article/details/126796762