当前位置: 首页 > news >正文

<C++>智能指针

1. 智能指针

#define _CRT_SECURE_NO_WARNINGS 
#include<iostream>
#include<memory>
using namespace std;int div()
{int a, b;cin >> a >> b;if (b == 0)throw invalid_argument("除0错误");return a / b;
}void func()
{int* p1 = new int[10]; // 这里亦可能会抛异常int* p2 = new int[10]; // 这里亦可能会抛异常int* p3 = new int[10]; // 这里亦可能会抛异常int* p4 = new int[10]; // 这里亦可能会抛异常try{div();}catch (...){delete[] p1;delete[] p2;delete[] p3;delete[] p4;throw;}delete[] p1;delete[] p2;delete[] p3;delete[] p4;
}int main()
{try{func();}catch (const exception& e){cout << e.what() << endl;// ...}return 0;
}

上面这个解决方法很繁琐

我们引入智能指针

RAII是个指导思想:获取资源之后去初始化一个对象,将资源交给对象管理

能用于智能指针、lock(),unlock()

//SmartPtr.h
namespace tyyg
{template<class T>class SmartPtr{public:SmartPtr(T* ptr): _ptr(ptr){}~SmartPtr(){cout << "~SmartPtr()" << endl;delete _ptr;}private:T* _ptr;};
}// test.c
#include "SmartPtr.h"
double div()
{double a, b;cin >> a >> b;if (b == 0)throw invalid_argument("除0错误");return a / b;
}void func()
{tyyg::SmartPtr<int> sp1(new int);tyyg::SmartPtr<int> sp2(new int);tyyg::SmartPtr<int> sp3(new int);// sp3的new抛异常,跳到catch的地方,sp1,sp2出作用域调用析构tyyg::SmartPtr<int> sp4(new int);tyyg::SmartPtr<pair<string, int>> sp5(new pair<string, int>("sort", 1));// div()抛异常,sp1,sp2,sp3,sp4出作用域调用析构,不会造成内存泄漏cout << div() << endl;
}int main()
{try{func();}catch (const exception& e){cout << e.what() << endl;// ...}return 0;
}

问题:拷贝

浅拷贝同一块资源会被析构两次,

回忆一下list的迭代器,我们用的是浅拷贝但它没问题,是因为我们不用迭代器负责节点的释放

但我们还是需要浅拷贝来拷贝智能指针,不然就不是管着这块资源

但我们分不清这是别人交给我们管理的,还是别人跟我们一起管理的,这就导致我们不知道该由谁来负责释放资源

如何解决:

1.1 auto_ptr

C++98 auto_ptr 管理权转移,被拷贝对象悬空(交给你管,我不管了)

template<class T>
class auto_ptr
{
public:auto_ptr(T* ptr): _ptr(ptr){}~auto_ptr(){cout << "~auto_ptr()" << endl;delete _ptr;}// sp2(sp1)auto_ptr(auto_ptr<T>& sp): _ptr(sp._ptr){sp._ptr = nullptr;}// 像指针一样T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;
};
int main()
{tyyg::auto_ptr<int> sp1(new int);tyyg::auto_ptr<int> sp2 = sp1;// sp1把资源给sp2管,sp1没用了// sp1悬空//*sp1 = 20;// 不能这样写return 0;
}

boost

C++11

C++11出来之前,boost搞除了更好用的scoped_ptr/shared_ptr/weak_ptr
C++11将boost库中智能指针精华部分吸收了过来

1.2 unique_ptr

核心原理:不让拷贝 --> 拷贝编译就报错

namespace tyyg
{template<class T>class unique_ptr{public:// RAII思想unique_ptr(T* ptr):_ptr(ptr){}~unique_ptr(){if (_ptr){cout << "delete" << _ptr << endl;delete _ptr;_ptr = nullptr;}}// 像指针一样T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T* get(){return _ptr;}//private://	// sp2(sp1)//	// C++98//	// 1、只声明,不实现//	// 2、声明成私有//	unique_ptr(const unique_ptr<T>& sp);unique_ptr(const unique_ptr<T>& sp) = delete;unique_ptr<T>& operator=(const unique_ptr<T>& sp) = delete;private:T* _ptr;};
}//C++11 unique_ptr 
// 核心原理:不让拷贝  --> 拷贝编译就报错
int main()
{tyyg::unique_ptr<int> up1(new int);tyyg::unique_ptr<int> up2(up1);// 报错return 0;
}

1.3 shared_ptr

原理:引用计数(string博客里写过)

count要一个资源一个计数

静态的static int _count不行,因为构造多个独立的对象时只会析构一次
如果是静态的计数,sp3构造时将count改成1,之后sp3析构,count变成0,再往后sp2和sp1指向的资源不释放

只能用int* _count

但也有缺陷,就是多线程的时候_count有可能会加错

namespace tyyg
{template<class T>class shared_ptr{public:void Release(){if (--(*_pCount) == 0 && _ptr){cout << "delete" << _ptr << endl;delete _ptr;_ptr = nullptr;delete _pCount;_pCount = nullptr;}}// RAII思想shared_ptr(T* ptr):_ptr(ptr), _pCount(new int(1)){}~shared_ptr(){Release();}shared_ptr(const shared_ptr<T>& sp): _ptr(sp._ptr), _pCount(sp._pCount){(*_pCount)++;}// sp1 = sp3shared_ptr<T>& operator=(const shared_ptr<T>& sp){//if (this != &sp)if (_ptr != sp._ptr){Release();_ptr = sp._ptr;_pCount = sp._pCount;++(*_pCount);}return *this;}// 像指针一样T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T* get() const{return _ptr;}int use_count(){return *_pCount;}private:T* _ptr;int* _pCount;};
}
//shared_ptr 引用计数,
// 析构的时候计数,最后一个析构对象释放资源
int main()
{tyyg::shared_ptr<int> sp1(new int);tyyg::shared_ptr<int> sp2(sp1);// 静态的count不行,因为构造多个独立的对象时只会析构一次tyyg::shared_ptr<int> sp3(new int);// 如果是静态的计数,sp3将count改成1,之后sp3析构,count变成0,再往后sp2和sp1指向的资源不释放sp1 = sp3;return 0;
}

新问题:循环引用

struct ListNode
{tyyg::shared_ptr<ListNode> _next = nullptr;tyyg::shared_ptr<ListNode> _prev = nullptr;int _val = 0;~ListNode(){cout << "~ListNode()" << endl;}
};
int main()
{// 循环引用std::shared_ptr<ListNode> p1(new ListNode);std::shared_ptr<ListNode> p2(new ListNode);cout << p1.use_count() << endl;cout << p2.use_count() << endl;//p1->_next = p2;// p2和_next一起管理它//p2->_prev = p1;// p1和_prev一起管理它// 这就导致p1,p2析构的时候这两块空间还未析构,直到_next和_prev析构的时候这两块空间才会析构//(但_prev析构取决于_next;_next析构取决于_prev,矛盾了)// 这就导致空间没法释放return 0;
}

解决方法:别让_prev, _next来管理空间,它们的指向空间时不增加计数,引入weak_ptr来辅助

1.4 weak_ptr

shared_ptr的小弟

专门解决shared_ptr循环引用

weak_ptr拷贝shared_ptr,但不增加计数,

weak_ptr不参与资源管理

namespace tyyg
{// 不参与指向资源的释放管理template<class T>class weak_ptr{public:weak_ptr(): _ptr(nullptr){}weak_ptr(const shared_ptr<T>& sp): _ptr(sp.get()){}weak_ptr<T>& operator=(const shared_ptr<T>& sp)// 能接收shared_ptr,但不增加计数{if (_ptr != sp.get()){_ptr = sp.get();}return *this;}// 像指针一样T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;};
}struct ListNode
{tyyg::weak_ptr<ListNode> _next;// 解决循环引用的方法,把这里换成weak_ptrtyyg::weak_ptr<ListNode> _prev;int _val = 0;~ListNode(){cout << "~ListNode()" << endl;}
};int main()
{// 循环引用tyyg::shared_ptr<ListNode> p1(new ListNode);tyyg::shared_ptr<ListNode> p2(new ListNode);cout << p1.use_count() << endl;cout << p2.use_count() << endl;p1->_next = p2;// p2和_next一起管理它p2->_prev = p1;// p1和_prev一起管理它// 但p1->_next和p2->_prev都是weak_ptr类型的,能够调用weak_ptr<T>& operator=(const shared_ptr<T>& sp),实现增加指向但不计数cout << p1.use_count() << endl;cout << p2.use_count() << endl;return 0;
}

上面的模拟实现只考虑了最基本的情况,跟库里的weak_ptr根本比不了,我们模拟实现只是为了更好地理解它,而不是为了造出更好的轮子

shared_ptr要和weak_ptr一起才能解决循环引用问题!用的时候一定要小心

2. 定制删除器 – 仿函数

unique_ptr/shared_ptr 默认释放资源用的delete
如何匹配申请方式去对应释放呢?回忆一下排序时我们对各种类型排序的处理方法:仿函数

我们先用库里的unique_ptr试一下效果(这里写了一部分仿函数来封装是为了看清楚调用的是哪个函数)

class Date
{
public:~Date(){cout << "~Date()" << endl;}
private:int _year = 1;int _month = 1;int _day = 1;
};// unique_ptr/shared_ptr  默认释放资源用的delete
// 如何匹配申请方式去对应释放呢?template<class T>
struct DeleteArray
{void operator()(T* ptr){cout << "delete[]" << ptr << endl;delete[] ptr;}
};template<class T>
struct Free
{void operator()(T* ptr){cout << "free" << ptr << endl;free(ptr);}
};struct Fclose
{void operator()(FILE* ptr){cout << "fclose" << ptr << endl;fclose(ptr);}
};int main()
{std::unique_ptr<Date> up1(new Date);std::unique_ptr<Date, DeleteArray<Date>> up2(new Date[10]);std::unique_ptr<Date, Free<Date>> up3((Date*)malloc(sizeof(Date) * 10));std::unique_ptr<FILE, Fclose> up4((FILE*)fopen("Test.cpp", "r"));return 0;
}

然后就自己实现一下吧,其实也就在模板里加一个仿函数,再改一下析构函数

unique_ptr在类的构造参数支持定制删除器,我们的unique_ptr没法在在构造函数传参支持定制删除器,因为通过构造函数传入的仿函数在析构函数里也用不了

namespace tyyg
{template<class T, class D = default_delete<T>>class unique_ptr{public:// RAII思想unique_ptr(T* ptr):_ptr(ptr){}~unique_ptr(){if (_ptr){/*cout << "delete" << _ptr << endl;delete _ptr;*/D del;// unique_ptr的定制删除器del(_ptr);_ptr = nullptr;}}// 像指针一样T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T* get(){return _ptr;}unique_ptr(const unique_ptr<T>& sp) = delete;unique_ptr<T>& operator=(const unique_ptr<T>& sp) = delete;private:T* _ptr;};
}

相关文章:

<C++>智能指针

1. 智能指针 #define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<memory> using namespace std;int div() {int a, b;cin >> a >> b;if (b 0)throw invalid_argument("除0错误");return a / b; }void func() {int* p1 new in…...

1.分析vmlinux可执行文件是如何生成的? 2.整理内核编译流程:uImage/zImage/Image/vmlinx之间关系

一、分析vmlinux可执行文件是如何生成的&#xff1f; 1、分析内核的底层 makefile 如下&#xff1a; vmlinux: scripts/link-vmlinux.sh vmlinux_prereq $(vmlinux-deps) FORCE$(call if_changed,link-vmlinux)vmlinux_prereq: $(vmlinux-deps) FORCE发现vmlinux的生成主要依…...

数据结构4——线性表3:线性表的链式结构

基本概念 ​ 链式存储结构用一组物理位置任意的存储单元来存放线性表的数据元素。 ​ 这组存储单元既可以是连续的又可以是不连续的甚至是零散分布在任意位置上的。所以链表中元素的逻辑次序和物理次序不一定相同。而正是因为这一点&#xff0c;所以我们要利用别的方法将这些…...

weblogic 忘记密码重置密码

解决&#xff1a;weblogic 忘记密码 weblogic安装后&#xff0c;很久不用&#xff0c;忘记访问控制台的用户名或者密码&#xff0c;可通过以下步骤来重置用户名密码。 版本&#xff1a;WebLogic Server 11g 说明&#xff1a;%DOMAIN_HOME%&#xff1a;指WebLogic Server 域(…...

安卓开发之动态设置网络访问地址

之前开发程序联测测接口的时候&#xff0c;因为要和不同的后台人员调接口&#xff0c;所以经常要先把程序里的ip地址改成后台人员给我的。每次都要先修改ip地址&#xff0c;之后编译运行一下&#xff0c;才能测试。但要是换了个后台人员&#xff0c;或者同时和2个后台人员测接口…...

深度学习模型训练工作汇报(3.8)

进行数据的初始整理的准备 主要是进行伪序列字典的设置&#xff0c;以及训练数据集的准备。 期间需要的一些问题包括在读取文件信息的时候&#xff0c;需要跳过文件的第一行或者前两行&#xff0c;如果使用循环判断的话&#xff0c;会多进行n次的运算&#xff0c;这是不划算的…...

【ns-3】添加nr(5G-LENA)模块

文章目录前言1. 下载5G-LENA源代码2. 配置并重新构建ns-3项目参考文献前言 本篇以ns-3.37为例介绍如何在ns-3中添加nr&#xff08;5G-LENA&#xff09;模块 [1]。5G-LENA是一个由Mobile Networks group CTTC&#xff08;Centre Tecnolgic de Telecomunicacions de Catalunya&a…...

(枚举)(模拟)(前缀和)(数组模拟哈希)(可二分)1236. 递增三元组

目录 题目链接 一些话 流程 套路 ac代码 题目链接 1236. 递增三元组 - AcWing题库 一些话 int f[N]; memset(f,0,sizeof f)影响不到f[N] 所以尽量不要对f[N]赋值&#xff0c;不要用f[N]操作 流程 //由三重暴力i,j,k因为三重暴力底下是分别用i和j&#xff0c;j和k作比较…...

mysql五种索引类型(实操版本)

为什么使用索引 最近学习了Mysql的索引&#xff0c;索引对于Mysql的高效运行是非常重要的&#xff0c;正确的使用索引可以大大的提高MySql的检索速度。通过索引可以大大的提升查询的速度。不过也会带来一些问题。比如会降低更新表的速度&#xff08;因为不但要把保存数据还要保…...

微服务进阶之 SpringCloud Alibaba

文章目录微服务进阶&#x1f353;SpringCloud 有何劣势&#xff1f;&#x1f353;SpringCloud Alibaba 提供了什么&#xff1f;提示&#xff1a;以下是本篇文章正文内容&#xff0c;SpringCloud 系列学习将会持续更新 微服务进阶 &#x1f353;SpringCloud 有何劣势&#xff1…...

前端性能优化笔记2 第二章 度量

相关 Performance API 都在 window.performance 对象下 performance.now() 方法 精度精确到微妙获取的是把页面打开时间点作为基点的相对时间&#xff0c;不依赖操作系统的时间。 部分浏览器不支持 performance.now() 方法&#xff0c;可以用 Date.now() 模拟 performance.n…...

关于new和delete的一些思考,为什么不能在析构函数中调用delete释放对象的内存空间,new和delete的原理

最近在写代码的时候&#xff0c;觉得每次new出来的对象都需要去delete好麻烦&#xff0c;于是直接把delete写到了析构函数中&#xff0c;在析构函数里面写了句delete this&#xff0c;结果调用析构函数的时候死循环了&#xff0c;不是很理解原因&#xff0c;于是去研究了一下。…...

一场以数字技术深度影响和改造传统实业的新风口,正在开启

当数字经济的浪潮开始上演&#xff0c;一场以数字技术深度影响和改造传统实业的新风口&#xff0c;正在开启。对于诸多在互联网时代看似业已走入死胡同的物种来讲&#xff0c;可以说是打开了新的天窗。对于金融科技来讲&#xff0c;同样如此。以往&#xff0c;谈及金融科技&…...

【LeetCode】13. 罗马数字转整数

题目链接&#xff1a;https://leetcode.cn/problems/roman-to-integer/ &#x1f4d5;题目要求&#xff1a; 罗马数字包含以下七种字符: I&#xff0c; V&#xff0c; X&#xff0c; L&#xff0c;C&#xff0c;D 和 M。 例如&#xff0c; 罗马数字 2 写做 II &#xff0c;即…...

2023/3/8集合之TreeSet HashSet简介 不含代码

TreeSet : 底层是由TreeMap维护的 无序的,不可重的 底层结构 : 红黑树(平衡二叉树) 特点 : 查询效率高,默认升序排序引用场景 : 适合应用在存储多个单个值的数据的集合,去重的,自动升序排序的场景新增方法:新增了一些与比较大小相关的方法 遍历方式 1)foreach 2)iterator 1测试…...

【面试1v1实景模拟】面试中常见的Java关键字详解

笑小枫专属目录老面&#x1f474;&#xff1a;Java中有哪些关键字老面&#x1f474;&#xff1a;简单介绍一下 final 关键字老面&#x1f474;&#xff1a;简单介绍一下 this、super 关键字老面&#x1f474;&#xff1a;简单介绍一下 static 关键字老面&#x1f474;&#xff…...

MySQL8.0.16存储过程比5.7.22性能大幅下降

MySQL8.0.16存储过程比5.7.22性能大幅下降 1、背景 从5.7.22迁移数据库到8.0.16&#xff0c;发现存储过程执行性能大幅下降。原来在5版本上执行只需要3-5秒&#xff0c;到8版本上居然要达到上万秒。 5版本&#xff1a; call Calculation_Week() OK 时间: 3.122s 8版本&#x…...

基于MATLAB的无线信道的传播与衰落(附完整代码与分析)

目录 一. 一般路径损耗模型 1. 1自由环境下路径损耗 1. 2 考虑实际情况 1.3 考虑阴影衰落 二. 代码仿真与理解 &#xff08;1&#xff09;函数文件 &#xff08;2&#xff09;函数文件 &#xff08;3&#xff09;主运行文件 三. 运行结果及理解 3.1 3.2 3.3 一. …...

SDX62如何查看Kernel版本和Operating System Version Patch Level

Kernel版本号方法一&#xff1a;adb shell登录&#xff0c;然后执行uname -a# uname -aLinux sdxlemur 5.4.180-perf #1 PREEMPT Fri Mar 3 04:24:42 UTC 2023 armv7l GNU/Linux方法二&#xff1a;内核源码查看&#xff0c;apps_proc/src/kernel/msm-5.4/Makefile 文件&#xf…...

001+limou+HTML——(1)HTML入门知识

000、本人编写前言 前言&#xff1a;本笔记来源于莫振杰的书《HTML、CSS、Javascript从零到一快速上手》&#xff0c;经过修改制成的自学笔记&#xff0c;本书很适合小白学习入门web的相关知识&#xff0c;你也可以先看看我从中学到了什么&#xff0c;再考虑是否去认真学习这本…...

大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计

随着大语言模型&#xff08;LLM&#xff09;参数规模的增长&#xff0c;推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长&#xff0c;而KV缓存的内存消耗可能高达数十GB&#xff08;例如Llama2-7B处理100K token时需50GB内存&a…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)

前言&#xff1a; 最近在做行为检测相关的模型&#xff0c;用的是时空图卷积网络&#xff08;STGCN&#xff09;&#xff0c;但原有kinetic-400数据集数据质量较低&#xff0c;需要进行细粒度的标注&#xff0c;同时粗略搜了下已有开源工具基本都集中于图像分割这块&#xff0c…...

免费PDF转图片工具

免费PDF转图片工具 一款简单易用的PDF转图片工具&#xff0c;可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件&#xff0c;也不需要在线上传文件&#xff0c;保护您的隐私。 工具截图 主要特点 &#x1f680; 快速转换&#xff1a;本地转换&#xff0c;无需等待上…...

深度学习水论文:mamba+图像增强

&#x1f9c0;当前视觉领域对高效长序列建模需求激增&#xff0c;对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模&#xff0c;以及动态计算优势&#xff0c;在图像质量提升和细节恢复方面有难以替代的作用。 &#x1f9c0;因此短时间内&#xff0c;就有不…...

DingDing机器人群消息推送

文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人&#xff0c;点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置&#xff0c;详见说明文档 成功后&#xff0c;记录Webhook 2 API文档说明 点击设置说明 查看自…...

【C++进阶篇】智能指针

C内存管理终极指南&#xff1a;智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...

简单介绍C++中 string与wstring

在C中&#xff0c;string和wstring是两种用于处理不同字符编码的字符串类型&#xff0c;分别基于char和wchar_t字符类型。以下是它们的详细说明和对比&#xff1a; 1. 基础定义 string 类型&#xff1a;std::string 字符类型&#xff1a;char&#xff08;通常为8位&#xff09…...

【笔记】AI Agent 项目 SUNA 部署 之 Docker 构建记录

#工作记录 构建过程记录 Microsoft Windows [Version 10.0.27871.1000] (c) Microsoft Corporation. All rights reserved.(suna-py3.12) F:\PythonProjects\suna>python setup.py --admin███████╗██╗ ██╗███╗ ██╗ █████╗ ██╔════╝…...

Qt学习及使用_第1部分_认识Qt---Qt开发基本流程

前言 学以致用,通过QT框架的学习,一边实践,一边探索编程的方方面面. 参考书:<Qt 6 C开发指南>(以下称"本书") 标识说明:概念用粗体倾斜.重点内容用(加粗黑体)---重点内容(红字)---重点内容(加粗红字), 本书原话内容用深蓝色标识,比较重要的内容用加粗倾…...

中国政务数据安全建设细化及市场需求分析

(基于新《政务数据共享条例》及相关法规) 一、引言 近年来,中国政府高度重视数字政府建设和数据要素市场化配置改革。《政务数据共享条例》(以下简称“《共享条例》”)的发布,与《中华人民共和国数据安全法》(以下简称“《数据安全法》”)、《中华人民共和国个人信息…...