[C++] 智能指针 进阶

news2025/4/2 18:34:27

标题:[C++] 智能指针 进阶
水墨不写bug

在这里插入图片描述


在很久之前我们探讨了智能指针的浅显认识,接下来会更加深入,从源码角度认识智能指针,从而了解智能指针的设计原理,并应用到以后的工作项目中。

本文将会按照C++智能指针的发展历史,回顾智能指针的各个版本的设计。后半部分将会模拟实现最常考的shared_ptr的功能。


一、C++智能指针的发展历程

1. C++98时代:auto_ptr的尝试与缺陷

背景:C++早期依赖手动new/delete,稍微有管理不当比如忘记释放,或者有异常抛出退出调用的函数栈,但是堆区的指针却找不到了),就可能导致内存泄漏。C++98引入auto_ptr,首次尝试自动化资源管理。
机制:基于RAII(资源获取即初始化),在构造时获取内存,在析构时自动释放内存
问题

  • 隐式所有权转移:复制auto_ptr时,原指针变为nullptr,导致难以追踪的悬空指针。
  • 不兼容容器:因拷贝语义异常,无法安全用于STL容器。

示例

auto_ptr<int> p1(new int(32));
auto_ptr<int> p2 = p1; // p1变为nullptr,后续使用p1导致未定义行为

结局:最终C++11弃用,C++17移除。因此为了代码的逻辑性和健壮性,以及可移植性,非常不建议使用auto_ptr


2. Boost库的智能指针

背景:C++社区通过Boost库探索更健壮的解决方案,后来这些方案被C++委员会收录到了C++11
关键类型

  • scoped_ptr:禁止拷贝,严格独占所有权,轻量且安全。
  • shared_ptr:引用计数实现共享所有权,解决多所有者场景。
  • weak_ptr:打破shared_ptr循环引用,防止内存泄漏。

影响:直接为C++11智能指针奠定基础。


3. C++11:C++划时代的进步

核心类型

  • unique_ptr:取代auto_ptr,独占所有权,支持移动语义,禁止拷贝,可管理数组(unique_ptr<T[]>)。
    unique_ptr<int> p1(new int(10));
    unique_ptr<int> p2 = std::move(p1); // 显式所有权转移
    
  • shared_ptr:引用计数共享资源,线程安全但性能有开销。
    shared_ptr<A> a = make_shared<A>();
    shared_ptr<A> b = a; // 引用计数增至2
    
  • weak_ptr:可通过其获取对应的shared_ptr资源,不增加计数,需通过lock()获取临时shared_ptr
    weak_ptr<A> w = a;
    if (shared_ptr<A> tmp = w.lock()) { /* 使用tmp */ }
    

改进

  • 弃用auto_ptr,推荐unique_ptr
  • 引入make_shared:合并控制块与对象内存分配,提升性能与异常安全。

4. C++14:完善与便利性增强

  • make_unique:填补make_shared的对称性,安全构造unique_ptr
    auto p = make_unique<int>(20); // 避免显式new
    
  • shared_ptr增强:支持自定义删除器与分配器,更灵活的资源管理。

后期的C++17用法不多常见,在此就不再赘述了。

二、智能指针的模拟实现

1、unique_ptr


template<class T>
class UniquePtr
{
    UniquePtr<T>& operator=(const UniquePtr<T>&) = delete;//删除拷贝构造和赋值重载
    UniquePtr(const UniquePtr<T>&) = delete;//这是unique_ptr的标志性的特点
public:
    UniquePtr(T* ptr)
        :_ptr(ptr)//浅拷贝
        //这就要求我们需要在外部new出堆区的空间,然后传递给智能指针来管理
    {}
    ~UniquePtr()
    {
        if(_ptr)//析构
            delete _ptr;
    }
    T& operator*()//重载*和->让智能指针(类)像普通指针(自定义类型)一样使用
    {
        return *_ptr;
    }
    T* operator->()
    {
        return ptr;
    }
private:
    T* _ptr;
};

2、shared_ptr


template<class T>
class SharedPtr
{
public:
    SharedPtr(T* ptr)
        :_ptr(ptr)//要求从外部传一个指向堆区的指针
        ,_refcount(new int{1})//开辟在堆上,便于所有的管理同一资源的智能指针对象维护
        ,_pmtx(new std::mutex)//同样的原因
    {}

    SharedPtr(const SharedPtr<T>& obj)
        :_ptr(obj._ptr)//只需要浅拷贝,然后引用计数++
        ,_refcount(obj._refcount)
        ,_pmtx(obj._pmtx)
    {
        AddRef();
    }

    SharedPtr<T>& operator=(const SharedPtr<T>& obj)
    {
        if(obj._ptr != _ptr)//不能给自身赋值
        {
            Release();//释放掉被赋值的智能指针对象的原有资源
            //浅拷贝
            _ptr = obj._ptr;//两个指针指向同一个T*对象
            _refcount = obj._refcount;//两个指针指向同一个int*
            _pmtx = obj._pmtx;//两个指针指向同一个mutex*的锁
            AddRef();
        }
    }
    void AddRef()
    {//增加引用计数需要加锁
        _pmtx->lock();
        (*_refcount)++;
        _pmtx->unlock();
    }

    void Release()
    {
        _pmtx->lock();
        bool f = false;
        if(--(*_refcount) == 0 && _ptr)
        {
            f = true;
            delete _ptr;
            delete _refcount;
        }
        _pmtx->unlock();
        //判断是否资源已经被释放,资源已经被释放,则释放锁
        if(f == true)
        {
            delete _pmtx;
        }
    }
    ~SharedPtr()
    {
        Release();
    }
    int UseCount()
    {
        return *_refcount;
    }
    T& operator*()//重载运算符
    {
        return *_ptr;
    }
    T* operator->()
    {
        return _ptr;
    }
    T* GetPtr()
    {
        return _ptr;
    }
private:
    T* _ptr;//指向堆区资源的裸指针
    int* _refcount;//开辟在堆区的引用计数
    std::mutex* _pmtx;//堆区的锁,考虑线程安全
    //如果设置在栈上,那么每一个对象都有一个独立的引用计数
    //每一次自增自减都需要对同一引用计数的所有对象维护,非常难以维护
};

3、weak_ptr

库中的wear_ptr的实现需要结合起来shared_ptr,shared_ptr内部含有两个引用计数,一个是记录管理资源的shared_ptr(占用引用计数)的个数,一个记录指向资源的weak_ptr(不占用引用计数)的个数。
在这里,为了简便而言,我们实现一个简化版本的weak_ptr,对上面的知识理解即可。


// 简化版本的weak_ptr实现
template<class T>
class WeakPtr
{
public:
    WeakPtr()
        :_ptr(nullptr)
    {}

    WeakPtr(const SharedPtr<T>& sp)
        :_ptr(sp.get())
    {}

    WeakPtr<T>& operator=(const SharedPtr<T>& sp)
    {
        _ptr = sp.get();
        return *this;
    }
    T& operator*()
    {
        return *_ptr;
    }
    T* operator->()
    {
        return _ptr;
    }
private:
    T* _ptr;
};


~完
转载请注明出处

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2324408.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

kubernetes》》k8s》》 kubeadm、kubectl、kubelet

kubeadm 、kubectl 、kubelet kubeadm、kubectl和kubelet是Kubernetes中不可或缺的三个组件。kubeadm负责集群的快速构建和初始化&#xff0c;为后续的容器部署和管理提供基础&#xff1b;kubectl作为命令行工具&#xff0c;提供了与Kubernetes集群交互的便捷方式&#xff1b;而…

C++中的new、malloc、realloc、calloc——特点?函数原型?释放方式?区别?校招面试常问内容?

作者&#xff1a;求一个demo 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 内容通俗易懂&#xff0c;没有废话&#xff0c;文章最后是面试常问内容&#xff08;建议通过标题目录学习&#xff09; 废话不多…

27_promise

插入一下前端助手测试&#xff0c;顺手可以用来做安全 promise promise 是一个es6新增的语法 汉语&#xff1a;承诺的意思 作用:是专门用来解决回调地狱!!!! 什么是回调函数&#xff1f; <script>// 回调函数 callback回调// 就是把函数A当作参数传递到函数B中// 在函…

leetcode刷题日记——跳跃游戏 II

[ 题目描述 ]&#xff1a; [ 思路 ]&#xff1a; 题目要求在一个一定能达到数组末尾的跳跃数组中(见55题 跳跃游戏)&#xff0c;找出能够跳到末尾的最小次数要求次数最少&#xff0c;那肯定是选取能选步数中最大的数。也就是在当前能够达到的距离中&#xff0c;选择能够达到的…

无人机进行航空数据收集对于分析道路状况非常有用-使用无人机勘测高速公路而不阻碍交通-

无人机进行航空数据收集对于分析道路状况非常有用-使用无人机勘测高速公路而不阻碍交通- 瑞士拥有1,400 多公里长的高速公路网络。这些公路将人和货物从山谷高原运送到阿尔卑斯山的最高山口。维护这些高速公路使国家得以顺利运转。高速公路维护的重要性显而易见&#xff0c;但在…

注意力蒸馏技术

文章目录 摘要abstract论文摘要简介方法预备知识注意力蒸馏损失注意力引导采样 实验结论总结参考文献 摘要 本周阅读了一篇25年二月份发表于CVPR 的论文《Attention Distillation: A Unified Approach to Visual Characteristics Transfer》,论文开发了Attention Distillation…

PERL开发环境搭建>>Windows,Linux,Mac OS

特点 简单 快速 perl解释器直接对源代码程序解释执行,是一个解释性的语言, 不需要编译器和链接器来运行代码>>速度快 灵活 借鉴了C/C, Basic, Pascal, awk, sed等多种语言, 定位于实用性语言,既具备了脚本语言的所有功能,也添加了高级语言功能 开源.免费 没有&qu…

鸿蒙项目源码-记账本app个人财物管理-原创!原创!原创!

鸿蒙记账项目源码个人财务管理含文档包运行成功ArkTS语言。 我一个月写的原创作品&#xff0c;请尊重原创。 原创作品&#xff0c;盗版必究&#xff01;&#xff01;&#xff01; api12 SDK5.0.0仅适用于最新的2024版本DevEco studio 共9个页面&#xff1a;广告倒计时页、登录、…

Ovito的python脚本

在 OVITO 里,Python 对象是构建脚本化操作的基础。下面为你详细介绍 OVITO 中 Python 对象的基本概念: 1. 数据管道(Pipeline) 数据管道是 OVITO 里最核心的对象之一。它就像一个流水线,把数据输入进来,经过一系列处理步骤,最后输出处理好的数据。 创建管道:借助 imp…

【免费】2007-2019年各省地方财政文化体育与传媒支出数据

2007-2019年各省地方财政文化体育与传媒支出数据 1、时间&#xff1a;2007-2019年 2、来源&#xff1a;国家统计局、统计年鉴 3、指标&#xff1a;行政区划代码、地区、年份、地方财政文化体育与传媒支出 4、范围&#xff1a;31省 5、指标说明&#xff1a;地方财政在文化、…

NOIP2007提高组.矩阵取数游戏

题目 492. 矩阵取数游戏 思路 不难发现, 每一行之间是独立的, 因此可以求出每一行的最大值, 然后行与行之间最大值相加, 就是总的最大值 对于行内来说, 每次可以选取左边或者右边, 可以使用区间 d p dp dp求解, 时间复杂度 O ( n 3 ) O(n ^ 3) O(n3), 因为列的最大值是 80 …

项目实战--权限列表

后端数据&#xff1a; 用表格实现权限列表 const dataSource [{key: 1,name: 胡彦斌,age: 32,address: 西湖区湖底公园1号,},{key: 2,name: 胡彦祖,age: 42,address: 西湖区湖底公园1号,}, ];const columns [{title: 姓名,dataIndex: name,key: name,},{title: 年龄,dataInd…

若依赖前端处理后端返回的错误状态码

【背景】 后端新增加了一个过滤器&#xff0c;用来处理前端请求中的session 若依赖存放过滤器的目录&#xff1a;RuoYi-Vue\ruoyi-framework\src\main\java\com\ruoyi\framework\security\filter\ 【问题】 后端返回了一个状态码为403的错误&#xff0c;现在前端需要处理这…

【计网】数据包

期末复习自用的&#xff0c;处理得比较草率&#xff0c;复习的同学或者想看基础的同学可以看看&#xff0c;大佬的话可以不用浪费时间在我的水文上了 1.数据包的定义&#xff1a; 数据包是网络通信中的基本单元&#xff0c;它包含了通过网络传输的所有必要信息。数据包的结构…

web权限划分提权和移权

前言&#xff1a;权限的基本认知 渗透权限划分&#xff1a;假如我们通过弱口令进入到web的后台 这样我们就拿到了web的管理员权限 管理员权限是web中最高的权限&#xff08;一般我们进入web的时候数据库会进行用户权限的划分&#xff1a;假设 0-10为最高的权限 11-10000为普通…

LocalDateTime序列化总结

版权说明&#xff1a; 本文由CSDN博主keep丶原创&#xff0c;转载请保留此块内容在文首。 原文地址&#xff1a; https://blog.csdn.net/qq_38688267/article/details/146703276 文章目录 1.背景2.序列化介绍常见场景关键问题 3.总体方案4.各场景实现方式WEB接口EasyExcelMybat…

[ 春秋云境 ] Initial 仿真场景

文章目录 靶标介绍&#xff1a;外网内网信呼oa永恒之蓝hash传递 靶标介绍&#xff1a; Initial是一套难度为简单的靶场环境&#xff0c;完成该挑战可以帮助玩家初步认识内网渗透的简单流程。该靶场只有一个flag&#xff0c;各部分位于不同的机器上。 外网 打开给的网址, 有一…

unity 截图并且展现在UI中

using UnityEngine; using UnityEngine.UI; using System.IO; using System.Collections.Generic; using System; using System.Collections;public class ScreenshotManager : MonoBehaviour {[Header("UI 设置")]public RawImage latestScreenshotDisplay; // 显示…

中断管理常用API(四)

一、request_irq(...) request_irq 函数主要用于硬中断相关操作&#xff0c;它的核心作用是把一个中断处理函数和特定的中断号进行绑定。当硬件设备触发该中断号对应的中断时&#xff0c;内核就会调用绑定的中断处理函数&#xff0c;像 irqhandler_func 这类。 此函数在多种硬件…

pyspark学习rdd处理数据方法——学习记录

python黑马程序员 """ 文件&#xff0c;按JSON字符串存储 1. 城市按销售额排名 2. 全部城市有哪些商品类别在售卖 3. 上海市有哪些商品类别在售卖 """ from pyspark import SparkConf, SparkContext import os import jsonos.environ[PYSPARK_P…