[C++11]:万字总结,过来人深刻的帮助理解智能指针

C++11 智能指针

这是我在项目中深刻理解智能指针的总结,希望对你有帮助。
请添加图片描述

在这里插入图片描述

引言

其实boost库早在C++11之前很久就开始提出了智能指针的概念,那么智能指针是什么,为什么会有智能指针,现在的指针还不够吗,还要专门提出一种智能指针的思想。,

指针可以说C/C++的基础,我们可以通过指针直接访问内存,准确的说是虚拟内存,指针是C/C++这套编程语言的根基,但是指针虽然特别好,但是这是一个双刃剑,有一个非常致命的问题,那就是C/C++中我们可以通过malloc free new delete来实现从堆中创建对象,释放对象,就是这个操作,导致了C/C++中内存隐患的问题,包括如今提出的Rust就是为了解决这个问题。

C++内存隐患

C++中我们在堆上创建了一个对象后,必须手动的释放,也就是说,malloc之后必须delete ,不然会造成什么问题,这个问题就是内存泄漏,我们创建了一个空间,但是没有释放,操作系统认为我们在使用这个空间,但是实际上我们并没有利用,这样就导致这块空间实际上就是没有利用,占用了资源。

为什么容易造成内存泄露

    1. throw try catch机制:C++在抛出异常的时候,会直接弹出,我们难道在每个throw的位置都加上delete吗?可以是可以,但是代码太冗余了。
    void func(int val)
    {
    	int* p = new int();
    	if(val == 0)
    	{
    		delete p;
    		throw error();
    	}
    	delete p;
    }
    
    1. 多线程环境:在多线程,多个执行流(线程)访问同一个资源,那么资源的回收时非常复杂的,如果判断没有线程使用它,来进行资源的回收,这也是一个问题。

智能指针

为了解决上述的问题,C++11提出了智能指针的设计,谈到智能指针,绕不开的就是RAII的思想、
RAII的思想

RAII(Resource Acquisition Is Initialization)是由c++之父Bjarne Stroustrup提出的,中文翻译为资源获取即初始化,他说:使用局部对象来管理资源的技术称为资源获取即初始化;这里的资源主要是指操作系统中有限的东西如内存、网络套接字等等,局部对象是指存储在栈的对象,它的生命周期是由操作系统来管理的,无需人工介入;

不愧是C++的设计者,这个思想真的很厉害,RAII通俗的理解方式是什么呢?就是创建一个类来管理对应元素的生命周期。

比如智能指针的构想就是,设计一个类来管理指针,通过运算符的重载,他支持指针的所有操作类型,通过在构造的时候,他通过构造函数创建一个指针,在析构的时候释放指针。
这样函数栈帧结束的时候,他就会调用类的析构函数,实现指针的释放,统一资源的回收。

auto_ptr

第一版的智能指针,存在很多的问题,现在已经几乎不再使用,但是他是先驱。

在这里插入图片描述
看看他的接口

在这里插入图片描述
重载了指针的各种操作。

但是在实际开发中我们发现了auto_ptr的局限性,所以针对智能指针进行功能的分类,具体分为shared_ptr, unique_ptr, weak_ptr

unique_ptr

unique的意思是独特的,这个指针表明,他只能够创建一次,不支持拷贝和赋值重载,他是独特的,独有的,但是移动构造显然是支持的。

创建他的方式

std::unique_ptr<int> pi(new int()); // C++11
std::unique_ptr<int> pi = std::make_unique<int>(); // C++14
模拟实现

unique_ptr的模拟实现

#ifndef __UNIQUE_PTR__
#define __UNIQUE_PTR__
#include <iostream>
namespace mycode
{
    template <class PtrType>
    struct Deltor
    {
        void operator()(PtrType *ptr)
        {
            delete ptr;
        }
    };

    template <class PtrType>
    struct Deltor<PtrType[]>
    {
        void operator()(PtrType *ptr)
        {
            delete[] ptr;

        }
    };
    template <class PtrType, class Del = Deltor<PtrType>>
    class unique_ptr
    {
    public:
        unique_ptr(PtrType *ptr) : _ptr(ptr) {}

        using Ref = PtrType &;
        using Ptr = PtrType *;
        using self = unique_ptr<PtrType, Del>;

        Ref operator*() { return *_ptr; }
        Ptr operator->() { return _ptr; }
        operator PtrType *() { return _ptr; }

        ~unique_ptr()
        {
            _deltor(_ptr);
        }

        // 禁止拷贝构造和拷贝赋值
        unique_ptr(const self &) = delete;
        unique_ptr &operator=(const self &) = delete;

        // 允许移动构造和移动赋值
        unique_ptr(self &&other) noexcept : _ptr(other._ptr)
        {
            other._ptr = nullptr;
        }

        self &operator=(self &&other) noexcept
        {
            if (this != &other)
            {
                _deltor(_ptr);
                _ptr = other._ptr;
                other._ptr = nullptr;
            }
            return *this;
        }

    private:
        PtrType *_ptr;
        Del _deltor; // 用于释放指针
    };
    template <class PtrType, class Del>
    class unique_ptr<PtrType[], Del>
    {
    public:
        unique_ptr(PtrType *ptr) : _ptr(ptr) {}

        using Ref = PtrType &;
        using Ptr = PtrType *;
        using self = unique_ptr<PtrType[], Del>;

        Ref operator*() { return *_ptr; }
        Ptr operator->() { return _ptr; }
        operator PtrType *() { return _ptr; }

        ~unique_ptr()
        {
            _deltor(_ptr);
        }

        // 禁止拷贝构造和拷贝赋值
        unique_ptr(const self &) = delete;
        unique_ptr &operator=(const self &) = delete;

        // 允许移动构造和移动赋值
        unique_ptr(self &&other) noexcept : _ptr(other._ptr)
        {
            other._ptr = nullptr;
        }

        self &operator=(self &&other) noexcept
        {
            if (this != &other)
            {
                _deltor(_ptr);
                _ptr = other._ptr;
                other._ptr = nullptr;
            }
            return *this;
        }

    private:
        PtrType *_ptr;
        Del _deltor; // 用于释放指针
    };

}
#endif

shared_ptr

有了unique_ptr当然还有shared, 可以分享的支持,我可以拷贝你,也就是我可以传值传参。

怎么实现呢?
请添加图片描述

引用计数

我们在类中添加一个计数器,当调用拷贝构造和赋值重载的时候我们计数器++,析构的时候,计数器–,如果减到0,那么我们就调用析构函数,释放对象。

具体实现
shared_ptr

#include <iostream>
#ifndef __SHARED_PTR__
#define __SHARED_PTR__
#include "unique_ptr.hpp"
namespace mycode
{
    template <class PtrType, class Del = Deltor<PtrType>>
    class shared_ptr
    {
    public:
        shared_ptr(PtrType *ptr) : _ptr(ptr), _pcount(new int(1))
        {
        }
        using Ref = PtrType &;
        using Ptr = PtrType *;
        using self = shared_ptr<PtrType, Del>;

        Ref operator*() { return *_ptr; }
        Ptr operator->() { return _ptr; }
        operator PtrType *() { return _ptr; }

        ~shared_ptr()
        {
            if ((*_pcount) > 1)
                (*_pcount)--;
            else
            {
                _deltor(_ptr);
                delete _pcount;
                _pcount = nullptr;
            }
        }

        // 禁止拷贝构造和拷贝赋值
        shared_ptr(const self &other) : _ptr(other._ptr)
        {
            (*_pcount)++;
        }
        self &operator=(const self &other)
        {
            if (this == &other)
                return *this;

            this->~shared_ptr(); // 将自身本身进行析构
            _ptr = other._ptr;
            (*_pcount)++;
            return *this;
        }

        // 允许移动构造和移动赋值
        shared_ptr(self &&other) noexcept : _ptr(other._ptr)
        {
            (*_pcount)++;
        }

        self &operator=(self &&other) noexcept
        {
            if (this == &other)
                return *this;
            this->~shared_ptr(); // 将自身本身进行析构
            _ptr = other._ptr;
            (*_pcount)++;
            return *this;
        }

    private:
        Ptr _ptr;
        int *_pcount = nullptr;
        Del _deltor; // 用于释放指针
    };
    template <class PtrType, class Del>
    class shared_ptr<PtrType[],Del>
    {
    public:
        shared_ptr(PtrType *ptr) : _ptr(ptr), _pcount(new int(1))
        {
        }
        using Ref = PtrType &;
        using Ptr = PtrType *;
        using self = shared_ptr<PtrType, Del>;

        Ref operator*() { return *_ptr; }
        Ptr operator->() { return _ptr; }
        operator PtrType *() { return _ptr; }

        ~shared_ptr()
        {
            if ((*_pcount) > 1)
                (*_pcount)--;
            else
            {
                _deltor(_ptr);
                delete _pcount;
                _pcount = nullptr;
            }
        }

        // 禁止拷贝构造和拷贝赋值
        shared_ptr(const self &other) : _ptr(other._ptr)
        {
            (*_pcount)++;
        }
        self &operator=(const self &other)
        {
            if (this == &other)
                return *this;

            this->~shared_ptr(); // 将自身本身进行析构
            _ptr = other._ptr;
            (*_pcount)++;
            return *this;
        }

        // 允许移动构造和移动赋值
        shared_ptr(self &&other) noexcept : _ptr(other._ptr)
        {
            (*_pcount)++;
        }

        self &operator=(self &&other) noexcept
        {
            if (this == &other)
                return *this;
            this->~shared_ptr(); // 将自身本身进行析构
            _ptr = other._ptr;
            (*_pcount)++;
            return *this;
        }

    private:
        Ptr _ptr;
        int *_pcount = nullptr;
        Del _deltor; // 用于释放指针
    };
    // template <class PtrType, class Del>
    // class unique_ptr<PtrType[], Del>
    // {
    // public:
    //     unique_ptr(PtrType *ptr) : _ptr(ptr) {}

    //     using Ref = PtrType &;
    //     using Ptr = PtrType *;
    //     using self = unique_ptr<PtrType[], Del>;

    //     Ref operator*() { return *_ptr; }
    //     Ptr operator->() { return _ptr; }
    //     operator PtrType *() { return _ptr; }

    //     ~unique_ptr()
    //     {
    //         _deltor(_ptr);
    //     }

    //     // 禁止拷贝构造和拷贝赋值
    //     unique_ptr(const self &) = delete;
    //     unique_ptr &operator=(const self &) = delete;

    //     // 允许移动构造和移动赋值
    //     unique_ptr(self &&other) noexcept : _ptr(other._ptr)
    //     {
    //         other._ptr = nullptr;
    //     }

    //     self &operator=(self &&other) noexcept
    //     {
    //         if (this != &other)
    //         {
    //             _deltor(_ptr);
    //             _ptr = other._ptr;
    //             other._ptr = nullptr;
    //         }
    //         return *this;
    //     }

    // private:
    //     PtrType *_ptr;
    //     Del _deltor; // 用于释放指针
    // };

}
namespace StandardModule
{
    template <class T,class Del>
    using shared_ptr = mycode::shared_ptr<T,Del>;
#endif

weak_ptr

weak虚弱的,虚弱的指针,我们一般叫做弱指针,他并没有管理指针。

感觉unique_ptr shared_ptr已经够了为什么还要弱指针呢?他的存在是为了解决shared_ptr的坑。

shared_ptr的坑

最经典的坑就是你中有我,我中有你的坑?
情景
我有一个公司的类,还有员工的类,公司需要管理员工,所以公司中有员工的指针,但是员工可以通过公司辞职,员工也有公司的指针,如果这两种指针都用shared_ptr, 那么恭喜你,中奖了,重了智能指针最经典的大坑。

在这里插入图片描述
如果我们进行释放公司的时候,会发生什么?

释放公司的时候,因为员工还没有释放,所以公司的指针有两份(员工和自己), 所以公司不释放,员工释放的时候,因为公司还没有释放,公司拥有员工指针,所以,员工也有两个人持有,不释放,最终的结果就是谁都不释放,内存泄漏。

那么为什么会出现这个问题呢?
关键就是员工拥有公司指针这个问题上?员工并没有拥有公司,他只有拥有访问公司的机会,但是shared_ptr让他直接拥有了公司,所以, weak_ptr诞生了,weak_ptr不含有指针,但是拥有访问对象的机会,相当于提供了一个访问公司的接口。

这样不就解决了吗?员工中的公司使用weak_ptr, 公司含有员工使用shared_ptr。
在这里插入图片描述
从接口可以看出,weak_ptr最关键的接口就是expired, weak_ptr通过shared_ptr进行初始化,他的内部存储了访问这个shared_ptr的通道,当我们想要访问shared_ptr的时候,我们可以调用expired这个接口返回一个shared_ptr, 但是我们并不会==拥有公司,我们只有需要的时候访问公司的权利==。

deleter

在这里插入图片描述
智能指针能支持我们自定义析构器,比如我们自己写了一个内存池,我们希望的不是默认的delete , 而是我们自己内存池的dealloc, 这个时候就可以使用这个。

总结

这是我的一家之谈,当然也是看了一些别人观点的启发,如果有错误,希望指正,如果对你理解智能指针有帮助,那真的太棒了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值