C++智能指针之unique_ptr

news2025/1/31 3:12:16

C++智能指针之unique_ptr

  • 前言
  • 一、unique_ptr
    • 1.1 unique_ptr类的初始化
    • 1.2 unique_ptr禁止拷贝和赋值
    • 1.3 release、reset函数
    • 1.4 向unique_ptr传递删除器
    • 1.5 unique_ptr与动态数组的使用
  • 总结


前言

  在C++中,动态内存的申请和释放是通过运算符:new 和 delete 进行管理的。其中 new 负责申请内存,delete负责释放内存。

  动态内存的使用很容易出现问题,这主要在于你需要保证在正确的时间释放内存,这是比较困难的,如果你忘记释放内存,就会造成内存泄露;有时在还有指针引用内存的情况下我们就释放了它,在这种情况下就会产生引用非法内存的指针。

  为了更容易(同时也更安全)地使用动态内存,新的标准库提供了两种智能指针类型来管理动态对象,智能指针的行为类似普通指针,最主要的区别在于它负责自动释放所指向的对象。这两种智能指针都定义在 memory 头文件内。

  • 本文是对于unique_ptr的分析。

一、unique_ptr

&emsp: 一个 unique_ptr“拥有”它所指向的对象,与shared_ptr不同,某个时刻只能有一个unique_ptr指向一个给定对象。当unique_ptr 被销毁时,它所指向的对象也被销毁。

  • unique_ptr的操作
    在这里插入图片描述
    在这里插入图片描述

1.1 unique_ptr类的初始化

  unique_ptr没有类似shared_ptr中make_shared的标准库函数返回一个unique_ptr,我们定义一个unique_ptr时,需要将它绑定到一个new返回的指针上,并且不能直接将new的结果用赋值运算符“=”赋值给unique_ptr(即初始化方式必须采用直接初始化方式)。

unique_ptr<double> p1;//正确
unique_ptr<int> p2(new int(42));//正确
unique_ptr<int> p3 = new int(42);//错误

1.2 unique_ptr禁止拷贝和赋值

  因为unique_ptr所指向的对象只能有一个unique_ptr指针,也就是一个引用计数。因此unique_ptr不支持普通的拷贝和赋值操作

unique_ptr<string> p1(new string("HelloWorld"));
unique_ptr<string> p2(p1);//是错误
unique_ptr<string> p3;
p3 = p1;//错误

特殊情况:

虽然两个unique_ptr不可以同时指向同一个内存对象,但是可以将一个即将销毁的unqie_ptr指针拷贝或赋值给另一个unqie_ptr

  函数的参数传递和返回值就是一个很好的例子,因为在函数内部的unique_ptr指针随着作用域的结束会自动销毁,因此可以将其作为返回值,然后将内存传递给另一个unique_ptr指针管理。

unique_ptr<int> clone(int p)
{
    return unique_ptr<int>(new int(p));
}
 
/*unique_ptr<int> clone(int p)
{
    unique_ptr<int> ret(new int(p));
    return ret;
}*/
int main()
{
    unique_ptr<int> p = clone(10);
    cout <<*p << endl; //打印10
    return 0;
}

在这里插入图片描述

1.3 release、reset函数

  虽然unique_ptr之间不能拷贝与赋值。但是可以使用release和reset函数来将指针的所有权从一个(非const)unique_ptr转移给另一个unique。

release函数

  • 将当前的unique_ptr指针所指的内存置为空,并且对这块内存的所有权消失

  • 返回值:返回当前unique_ptr所指的内存

unique_ptr<string> p1(new string("Hello"));
 
unique_ptr<string> p2(p1.release());//p1将自己所指的内存空间置空,并且返回该内存空间。之后对该内存空间的操作权消失,从而p2得到该内存的权限

注意事项:

  因为release函数会使unque_ptr指针与内存之间的关系。所以unique_ptr调用release函数之后必须将返回值传递给另一个unqiue_ptr,否则就会内存泄露

unique_ptr<string> p1(new string("Hello"));
p1.release();//错误,虽然p1断开了与内存的关系,但是没有另一个unqieu_ptr来接手这块内存,造成内存泄漏
 
/*
改正:
unique_ptr<string> p2(p1.release()); //将p1的原来内存交给另一个unique_ptr管理
*/

reset函数

  • 用来重置当前unqie_ptr指针。

  • 重置之后接手另一块内存或者一直处于空状态

unique_ptr<string> p1(new string("Hello"));
p1.reset();//将p1置空,不指向内存对象
unique_ptr<string> p1(new string("Hello"));
p1.reset(nullptr);//同上
unique_ptr<string> p1(new string("Hello"));
unique_ptr<string> p2(new string("World"));
 
p1.reset(p2.release());//p2置空之后,然后p1也置空,然后p1来接手p2所指向的内存

1.4 向unique_ptr传递删除器

  与shared_ptr类相同,unique_ptr默认情况下会调用默认析构函数来释放(delete)自己所指向的对象。不过我们也可以通过重载来指定unqie_ptr的删除器。

  与shared_ptr重载删除器不同,unique_ptr重载删除器会影响到unique_ptr类型以及如何构造(或reset)该类型的对象。

  与重载关联容器的比较操作类似,我们必须在<>中unique_ptr指向类型之后提供删除器类型。

  在创建或reset一个这种unique_ptr类型对象时,必须提供一个指定类型的可调用对象(删除器):

//p指向一个类型为objT的对象,并使用一个类型为delT的对象释放objT对象
//它会调用一个名为fcn的delT类型对象
unique_ptr<objT,delT> p(new objT,fcn);

示例:

  我们使用decltype来指明函数类型,在后面加一个*代表函数指针

void f(destination &d) {
    connection c=connec(&d);
    unique_ptr<connection,decltype(end_connection)*> p(&c,end_connection);
 
    ...//使用这个连接
     //当f函数退出或者异常退出,p都会调用end_connection函数
}

1.5 unique_ptr与动态数组的使用

  标准库提供了一个可以管理new分配的数组的unique_ptr版本

unique_ptr<int[]> arr(new int[3]{ 1,2,3 }); //定义一个指向int型数组的智能指针对象
unique_ptr<int[]> arr2(new int[3]);
arr.release();  //自动调用delete[]销毁其指针
unique_ptr<int[]> arr= new int[3]{ 1,2,3 };  //错误
unique_ptr类操作数组的方法:

  动态数组的访问:unique_ptr操作数组,不提供点和箭头成员运算符,因为数组不是一个对象。但是可以通过下标运算符来访问操作数组

unique_ptr<int[]> arr(new int[3]{ 1,2,3 });
for (int i = 0; i < 3; ++i)
        arr[i] = i;

总结

参考:

  • C++ Primer 第五版 P417-P420

期待大家和我交流,留言或者私信,一起学习,一起进步!

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

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

相关文章

【无线传感器】基于Matlab实现WSN 查找两个节点之间的最短路径并发送数据

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

Linux基础内容(13)—— 进程控制

目录 1.fork函数的进程创建 1.fork返回值 2.fork返回值 3.fork调用失败 2.写时拷贝 3.退出码的知识 4.进程退出 1.退出的情况 2.正常退出 5.进程等待 1.调用系统等待函数杀死僵尸进程 2.僵尸状态与PCB的关系 3.进程阻塞等待与非阻塞等待方式 6.进程程序替换 1.替…

【网络篇】第十八篇——IP协议相关技术

目录 DNS DNS背景 域名的层级关系 域名解析过程 使用dig工具分析DNS过程 ARP DHCP NAT NAT IP转换过程 NAPT NAT技术的缺陷 如何解决NAT潜在问题 ICMP ICMP功能 ICMP协议格式 ping命令 traceroute命令 IGMP 跟IP 协议相关的技术也不少&#xff0c;接下来说说与IP 协议相关的重…

Docker——Prometheus监控服务治理

摘要 Prometheus是继Kubernetes之后&#xff0c;第二个从云原生计算基金会&#xff08;CNCF&#xff09;毕业的项目。Prometheus是Google监控系统BorgMon类似实现的开源版&#xff0c;整套系统由监控服务、告警服务、时序数据库等几个部分&#xff0c;及周边生态的各种指标收集…

uniapp vuex正确的打开方式

uniapp vuex正确的打开方式一、vuex与全局变量globalData的区别二、uniapp vuex使用目录结构如下1. 根目录创建vuex目录&#xff0c;创建index.js文件2. 模块化代码3. 在 main.js 中导入store文件4. 调用一、vuex与全局变量globalData的区别 uni-app像小程序一样有globalData&…

项目开发——【流程图】软件工程程序流程图详解《如何正确绘制项目开发流程图》

程序流程图详解 介绍&#xff1a;通过图形符号形象的表示解决问题的步骤和程序。好的流程图&#xff0c;不仅能对我们的程序设计起到作用&#xff1b;在帮助理解时&#xff0c;往往能起到"一张图胜过千言万语"的效果。 一、程序流程图基本控制结构 顺序型&#xf…

如何实现RTS游戏中鼠标在屏幕边缘时移动视角

文章目录&#x1f9e8; Preface&#x1f38f; 判断鼠标是否处于屏幕边缘⚽ 获取鼠标处于屏幕边缘时的移动方向&#x1f3a8; 控制相机在x、z轴形成的平面上移动&#x1f3d3; 完整示例代码&#x1f9e8; Preface 本文简单介绍如何在Unity中实现即时战略游戏中鼠标在屏幕边缘的…

创新赋能合作伙伴,亚马逊云科技re:Invent科技盛宴

北京时间11月29号&#xff0c;亚马逊云科技年度峰会re:Invent 2022将在拉斯维加斯开幕。这场年度最重磅的云计算技术大会不仅是科技盛宴&#xff0c;也是亚马逊云科技与诸多客户交流互鉴的绝佳平台&#xff0c;今天带大家认识一下几位资深云计算用户&#xff0c;以及他们和re:I…

MyBatis ---- 搭建MyBatis

MyBatis ---- 搭建MyBatis1. 开发环境2. 创建maven工程a>打包方式&#xff1a;jarb>引入依赖3. 创建MyBatis的核心配置文件4. 创建mapper接口5. 创建MyBatis的映射文件6. 通过junit测试功能7. 加入log4j日志功能a>加入依赖b>加入log4j的配置文件1. 开发环境 IDE&a…

Linus 文件处理(四)

目录 一、前言 二、高级主题: fcntl和mmap 1、fcntl 2、mmap 3、Using mmap 一、前言 本文将简单介绍Linux文件和目录&#xff0c;以及如何操作它们&#xff08;如何创建文件、打开、读、写和关闭&#xff0c;程序如何操作目录&#xff0c;如创建、扫描和删除目录等&…

池风水利用工具

引用 这篇文章的目的是介绍一种基于内核态内存的越界写入通用利用技术和相关工具复现. 文章目录引用简介分析调试分析漏洞利用工具使用方法工具使用效果相关引用参与贡献简介 笔者的在原作者池风水利用工具(以下简称工具)基础上进行二次开发,新增了全自动获取内核调试模块符号…

QT-QTableWidget中的cell和item的区别

文章目录QTableWidget中单击一个单元格响应不同的函数&#xff1a;cell和item的区别&#xff1a;单击单元格响应自定义函数我的错误思路&#xff1a;已剪辑自: https://blog.csdn.net/CCLasdfg/article/details/114691478 QTableWidget中单击一个单元格响应不同的函数&#xf…

【服务器搭建】教程三:怎样购买域名并怎样进行域名解析 来啦

前言 购买一台服务器&#xff0c;再来个域名&#xff0c;搭建一个自己的个人博客网站&#xff0c;把一些教程、源码、想要分享的好玩的放到网站上&#xff0c;供小伙伴学习玩耍使用。我把这个过程记录下来&#xff0c;想要尝试的小伙伴&#xff0c;可以按照步骤&#xff0c;自己…

多云加速云原生数仓生态,华为与 HashData 联合打造方案

多云的兴起&#xff0c;源于用户应用对于基础设施、云服务功能、安全性等的差异化需求&#xff0c;用户希望根据需求将应用、数据因“云”制宜&#xff0c;实现业务的高度灵活性和高效性。这也直接驱动着云原生数据仓库等一批云原生应用的流行&#xff0c;以及存储等基础设施加…

为什么用公钥加密却不能用公钥解密?

一直以来我都在逃避写HTTPS。 毕竟。 HTTPS里名词太多。概念又巨繁琐。 实在是太难解释了&#xff0c;能不写我尽量不写。。。。 但为了让图解网络的知识体系尽量完整些。 今天&#xff0c;大家忍一忍。 我们就从对称加密和非对称加密聊起吧。 对称加密和非对称加密 小学…

Python学习笔记-语言基础

1.基础语法特点 1.1 注释 注释&#xff0c;是用于在代码中添加的标注性的文字&#xff0c;程序中并不会执行&#xff0c;知识用于告知程序员&#xff0c;该代码是干什么的&#xff0c;怎么用的等。 注释主要用于防止别人阅读代码是可以识别到代码的意图或者一段时间之后我们…

教程五 在Go中使用Energy创建跨平台GUI - 执行开发者工具方法

教程-示例-文档 介绍 本文介绍在Energy中如何执行开发者工具方法 开发者工具方法&#xff0c;可以设置浏览器头&#xff0c;模拟仿真设备等. 使用方式 字典对象创建 cef.NewCefDictionaryValue() 字典对象是keyvalue方式, chromium定义的字典名称和对应的值&#xff0c;通…

APOLLO UDACITY自动驾驶课程笔记——感知、预测

1、计算机视觉 无人驾驶车有四个感知世界的核心任务&#xff1a;检测——指找出物体在环境中的位置&#xff1b;分类——指明确对象是什么&#xff1b;跟踪——指随时间的推移观察移动物体&#xff1b;语义分割——将图像中的每个像素与语义类别进行匹配如道路、汽车、天空。 …

Adb找不到设备解决方法

有时候使用adb devices 却找不到设备&#xff0c;而用一些第三方的软件却可以找的到&#xff0c; 除了没有打开USB调试模式&#xff0c;或者没装驱动&#xff0c;数据线有问题&#xff0c;操作系统是精简版系统外&#xff0c; 一般是因为设备的安卓版本太高&#xff0c;导致a…

操作系统内存管理-01分段

前言 本文讲述本文博主在学习80386下 window下段式内存管理。 内存管理往往需要软硬件结合进行管理&#xff0c;CPU定制一套官方规范&#xff0c;要求操作系统按要求实现某些操作即可。本文所述的CPU 分段规范 在intel 第三卷 第三章。 分段概述 我们知道每一个程序会被赋予…