智能指针

RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内 存、文件句柄、网络连接、互斥量等等)的简单技术。 在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在 对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。

这种做 法有两大好处:

  • 不需要显式地释放资源。
  • 采用这种方式,对象所需的资源在其生命期内始终保持有效。

先实现一个普通的智能指针

template<class T>
class smart_ptr
{
public:
    smart_ptr(T* ptr)
        :_ptr(ptr)
    {}

    ~smart_ptr()
    {
        if (_ptr)
        {
            delete _ptr;
            _ptr = nullptr;
        }
    }

    T& operator*()
    {
        return *_ptr;
    }

    T* operator->()
    {
        return _ptr;
    }

private:
    T* _ptr;
};

即使中间有异常抛出,当作用域走完生命周期,析构函数会自动调用。完成析构,就不会发生了内存泄漏问题。

unique_ptr 基本实现

template<class T>
class unique_ptr
{
public:
    unique_ptr(T* ptr)
        :_ptr(ptr)
    {}

    ~unique_ptr()
    {
        if (_ptr)
        {
            delete _ptr;
            _ptr = nullptr;
        }
    }

    T& operator*()
    {
        return *_ptr;
    }

    T* operator->()
    {
        return _ptr;
    }

    unique_ptr(unique_ptr& p) = delete;
    unique_ptr operator=(unique_ptr& p) = delete;

private:
    T* _ptr;
};

unique_ptr跟普通智能指针区别不大 只是unique_ptr是不允许用拷贝和赋值的

shared_ptr基本实现

它能使用拷贝和赋值 使用的是一种计数的方法。

    template<class T>
    class shared_ptr
    {
    public:
        shared_ptr(T* ptr)
            :_ptr(ptr)
            , pcount(new int(1))
        {}

        ~shared_ptr()
        {
            Relses();
        }

        T& operator*()
        {
            return *_ptr;
        }

        T* operator->()
        {
            return _ptr;
        }

        shared_ptr(shared_ptr& p)
            :_ptr(p._ptr)
            ,pcount(p.pcount)
        {
            (*pcount)++;
        }

        void Relses()
        {
            if (--(*pcount) == 0 && _ptr)
            {
                cout << _ptr << endl;
                delete _ptr;
                delete pcount;
                _ptr = nullptr;
                pcount = nullptr;
            }
        }

        shared_ptr& operator=(shared_ptr& p)
        {
            if (_ptr != p._ptr)//当两个指针地址不相同才赋值
            {
                Relses();

                _ptr = p._ptr;
                pcount = p.pcount;
                *(pcount)++;
            }
            return *this;
        }

    private:
        T* _ptr;
        int* pcount;
    };

weak_ptr基本实现

此智能指针是用来辅助shared_ptr的 用来解决循环引用问题

    template<class T>
    class weak_ptr
    {
    public:
        weak_ptr()
            :_ptr(nullptr)
        {}

        weak_ptr(const shared_ptr& p)
            :_ptr(p.get())
        {}

        weak_ptr& operator=(const shared_ptr& p)
        {
            if (_ptr != p.get())
            {
                _ptr = p.get();
            }
            return *this;
        }

        T& operator*()
        {
            return *_ptr;
        }

        T* operator->()
        {
            return _ptr;
        }

    public:
        T* _ptr;
    };

循环引用问题

    struct listnode
    {
        shared_ptr _next = nullptr;
        shared_ptr _prev = nullptr;

        ~listnode()
        {
            cout << "析构" << endl;
        }
    };

    shared_ptr < listnode > p1(new listnode);
    shared_ptr < listnode > p2(new listnode);
    p1->_next = p2;
    p2->_prev = p1;

智能指针

可以看到,这么本身要析构两次的,但没有释放,造成了内存泄漏

智能指针

p1指向next的空间

p2指向prev的空间

指针指针赋值

next管理着prev的值

prev也管理着next的值

这时管理next有2个 p1 和prev

这是管理prev有2个 p2和next

p1和p2释放后 next和prev还有1 所以无法释放

而shared_ptr是用计数方式。所以要释放时 管理某个地址的指针为0时,才可以释放此地址

而他们两个指针互相管理着,都在等着对方为0 造成了无法释放的问题 造成内存泄漏 这叫做循环引用问题

此时就要用到weak_ptr来辅助shared_ptr来解决

智能指针

weak_ptr的作用就是帮next和prev管理。不需要next和prev亲自去管理。

当weak_ptr帮忙管理时

这时管理next的有1个 p1

这时管理prev的有1个 p2

当p1和p2析构时,next和prev就自动释放了

weak只帮忙管理,不帮忙析构。

在不用拷贝时,就用unique_ptr,要拷贝就用shared_ptr

循环引用比较少见,遇到时,要用weak_ptr辅助帮忙。

定制删除器

可以自己写一个专门用来释放这个定制类

template<class T>
struct Deletea
{
public:
    void  operator()(T* ptr)
    {
        cout << "delete析构" << endl;
        delete[] ptr;
    }
};

template<class T>
struct Free
{
public:
    void operator()(T* ptr)
    {
        cout << "free析构" << endl;
        free(ptr);
    }
};

    struct test
    {
        ~test()
        {
            cout << "析构";
        }
        int a;
        int b;
    };

    unique_ptr> p1(new test[10]);
    unique_ptr> p2((test*)malloc(sizeof(test)*10));,free,deletea

Original: https://www.cnblogs.com/LonelyMoNan/p/16743363.html
Author: lemon-Breeze
Title: 智能指针

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/607469/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • PYTORCH: 60分钟 | 神经网络

    神经网络可以使用 torch.nn包构建。 现在你已经对autograd有所了解, nn依赖 autograd 定义模型并对其求微分。 nn.Module 包括层,和一个返回 ou…

    Linux 2023年6月16日
    0153
  • .NET使用StackExchange.Redis

    csharp;gutter:true; using StackExchange.Redis; using System; using System.Collections.Gene…

    Linux 2023年5月28日
    080
  • 【小记】腾讯云 Linux 虚拟机如何正确修改 hosts 文件

    如果直接修改 /etc/hosts 文件,重启后设置会丢失还原,原因是腾讯云虚拟机默认使用了 Cloud-Init 进行初始化操作。 参见:https://cloud.tencen…

    Linux 2023年6月13日
    080
  • shell echo单行和多行文字定向写入到文件中

    单行文本: #!/bin/bash echo "192.168.85.24 tsedb">> /etc/hosts 多行文本: < #!/bi…

    Linux 2023年5月28日
    072
  • Redis中使用redis-cli及密码登录

    使用redis-cli登录后如果Redis中设置了密码那么输入密码可能会出现: NOAUTH Authentication required的错。 这个时候可以输入:auth pa…

    Linux 2023年5月28日
    082
  • PHP实现无限级分类

    $array = array( array(‘id’ => 1, ‘pid’ => 0, ‘name’ => ‘河北省’), array(‘id’ => 2…

    Linux 2023年6月7日
    060
  • Linux下侦听端口被占用,怎么解决?

    不知道你有没有遇到过这种问题:在同一台物理机器上,服务A 启动时侦听 端口1 ,同时它也作为客户端去连接 服务B,连接服务B时候会随机一个端口号,假如随机的是 端口2 ,这个时候 …

    Linux 2023年6月13日
    086
  • [Git系列] Git 基本概念

    版本控制系统 版本控制系统是一种帮助软件开发者实现团队合作和历史版本维护的软件,一个版本控制系统应具备以下列出的这几个基本功能: 允许开发者并发工作; 不允许一个开发者覆写另一个开…

    Linux 2023年6月14日
    090
  • redis实战

    转载于:https://blog.csdn.net/piaoslowly/article/details/81563579 redis简介 Redis 是一个开源的 使用 ANSI…

    Linux 2023年5月28日
    087
  • C++的回调函数

    一、简介 本文主要介绍C++中如何使用回调函数。 二、回调函数介绍 回调函数主要在”回”字,和正常的函数调用方式不太一样。正常的函数由开发者自己定义返回类型…

    Linux 2023年6月7日
    084
  • Markdown基础语法

    Markdown语法 ## 欢迎使用Markdown编辑器 你好! 这是你第一次使用 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下M…

    Linux 2023年6月14日
    098
  • 【证券从业】金融基础知识-第五章 债券02

    注1:后续学习并整理到第八章,全书完结后再合并成一个笔记进行源文件分享 注2:本章内容巨多,大约分为两篇文章记录消化 posted @2022-06-09 23:55 陈景中 阅读…

    Linux 2023年6月13日
    082
  • VMware Workstation 与 Device/Credential Guard 不兼容

    问题: 在确定已经禁用 Hyper-V 功能的前提下,启动虚拟机还是报错 ” VMware Workstation 与 Device/Credential Guard …

    Linux 2023年6月7日
    082
  • Linux关于防火墙的命令

    Linux关于防火墙的命令 一、red hat/CentOs7关闭防火墙的命令 查看防火墙状态 systemctl status firewalld service iptable…

    Linux 2023年6月11日
    081
  • 服务器监控-TOP命令详解

    用jmeter进行性能测试的时候,top命令是最方便可用的服务器监控命令,可以看出服务器的当前负载量,CPU占用率,内存占用率等重要信息,下面详细解说一下top命令里面的各个指标。…

    Linux 2023年5月27日
    095
  • Java类初始化顺序小结

    第一种情况(单一类) 测试结果 &#x9759;&#x6001;&#x53D8;&#x91CF; &#x9759;&#x6001;&…

    Linux 2023年6月7日
    092
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球