第7章 Page446~449 7.8.9智能指针 std::unique_ptr

“unique_ptr”是“独占式智能指针”

名字透露身份,“unique_ptr”是“独占式智能指针”。使用它管理前面的O类指针:

演示1:

例中 p 是一个智能指针。其中的“<O>”指明它所指向的数据类型是“O”。除了创建方法不太一样,以及不用手工释放之外,智能指针使用上和它所管理的裸指针基本一样。

如果健忘而负责任的程序员,忘了p是智能指针,写出“delete p”这样的代码,也不用怕,编译器会就出这个错误。

例中的裸指针同样没有名字,在调用智能指针对象的构造函数是,直接使用new生成。这是推荐的做法,这样做的好处是,防止有人故意捣乱。

构建“unique_ptr”对象是,只能采用“构造式初始化”,不允许采用“赋值式初始化”

如果手头上确实已经有了一个裸指针,临时需要交给智能指针来管理,也是支持的:

O* po = new O();
std::unique_ptr <O> p(po);

这里有个细节,即构建“unique_ptr”对象是,只能采用“构造式初始化”,不允许采用“赋值式初始化”的语法:

std::unique_ptr <O> p2 = new O();//编译出错
O* po = new O();
std::unique_ptr <O> p = po; //同样编译出错
不允许将裸指针直接赋值给智能指针

这是刻意的设计,不允许将一个裸指针使用“=”赋值给智能指针。良好的智能指针就是这样一个纠结的设计:既要让它用起来就像一个裸指针,又要在关键的地方提醒一下你,它不是一个真的指针

智能指针也可以有“空指向”的状态,构建时不传入裸指针即可:

std::unique_ptr <O> p; //空指向的智能指针
if(p == nullptr) //成立
{cout << "p is a nullptr" << endl;
}
if(!p)//也成立
{cout << "not p" << endl;
}
演示unique_ptr初始还方式:

以上都是让智能指针尽量用起来像裸指针的设计。背后的实现手段还是“重载操作符”。

智能指针当然可以改变指向,

只是同样不能使用“=”将裸指针赋给智能指针右值必须同样是智能指针

std::unique_ptr <O> p; //空指向的智能指针
...
p = std::unique_ptr <O> (new O);//不能省略为“p = new O”

但是这样写显得很冗长,可以改用“unique_ptr”带参版“reset(...)”的方法,用于改变一个“unique_ptr”的指向:

O* src1 = new O;
O* src2 = new O;
std::unique_ptr <O> p(src1); //先管理src1
...
p.reset(src2); //改为管理src2,改之前,src1将被释放
演示智能指针改变指向:

改变一个“unique_ptr”的指向,如果该智能指针不是空指向,会先释放原来管理的裸指针,再接管新的裸指针。这中间隐藏了一个风险,即新指向和旧指向,必须确保不是同一个对象:

O* o = new O;
std::unique_ptr <O> p(o); //p管理着o
p = std::unique_ptr <O>(o); //p改变指向,但其实还是指向o。

003行执行过程是这样的:在改变指向之前,p要先干掉当前所管理的裸指针,也就是o。然后再管理新的裸指针,不幸的是,这个所谓的“新”的裸指,仍然是o,刚刚被干掉的o。编译器无法帮我们识别这样的问题。

“unique_ptr”的独占性:

一个裸指针不能由多个“std::unique_ptr”管理,但编译器挡不住程序员刻意将一个裸指针交给多个“unique_ptr”管理,编译器无法阻止你干出“一女多嫁”的事情:

O* o = new O; //一个裸指针
std::unique_ptr <O> p1(o); //交给p1管
std::unique_ptr <O> p2(o); //又交给p2管

错误将在运行时发生,本例中由于O结构没有任何成员数据,所以发生这个错误的程序可能不会挂掉,但错误确实发生了,能够从屏幕输出o被释放两次

演示“一女多嫁”:

能不能“改嫁”呢?

O* o = new O; //还是一个裸指针
std::unique_ptr <O> p1(o); //先交给p1
std::unique_ptr <O> p2 = p1; //然后由p1转交个p2?
//std::unique_ptr <O> p2(p1); //同上,但使用更正规构造式初始化

逻辑上是说的通的,具体做法可以是:p1让出o,让给p2; p1变成空指向,并且C++中已经被标为“废弃”,但暂时还可使用老版本的智能指针“std::auto_ptr”,就是这么设计的。但事实003行或004行,都将编译失败。

C++ 11认为,这样偷偷修改源对象的做法太隐晦了,程序员难以直观地通过阅读代码“想起”这过程中源对象(例中的p1)变成空指向了。

演示“改嫁”:

转移函数std::move()

如果确实需要转移管理全,有两种方法。一种是明确使用C++11提供的转移函数:

std::unique_ptr <O> p2 = std::move(p1); //OK
//也可以std::unique_ptr <O> p2(std::move(p1)); //OK

包装一层“std::move”的调用,已明提示阅读者,p1的内容被“转移(move)”了,这就是“std::unique_ptr”作为“独占式”智能指针的最经典表现,即:不能将独占式智能指针A,赋值给独占式智能指针B,只能做转移。源方失去对裸指针的管理权,目标方获得。一失一得,裸指针的管理权仍然只属一方。

演示std::move():

可以看到,裸指针只被释放了一次

release()方法:

实现转移管理权的另一种做法,是通过“std::unique_ptr”提供的“release()”方法。该方法可让一个智能指针“放手”它所“爱过”的裸指针:

std::unique_ptr <O> p(new O());
O* o = p.release(); //“吐出”所管理的裸指针

p吐出管理对象之后,自然变成空指向,而程序员手上拿着一个裸指针,又得考虑何时delete这个o对象。或者,干脆把它交给另一个智能指针管理吧,这是一个“迂回”的转移过程。

演示release():

get()方法:

如果临时需要得到裸指针,但又不希望智能指针撒手不管,可以使用“std::unique_ptr”的“get()”方法:

std::unique_ptr <O> p(new O());
O* o = p.get();
演示get()方法:

unique_ptr变成空指向:unique_ptr被赋值裸指针的一个特例

赋裸指针:

如果想让一个“unique_ptr”变成空指向,倒是可以直接为它赋值nullptr,尽管前面我们刚说过不允许为“unique_ptr”赋值裸指针(而理论上nullptr是裸指针),这算是一个特例。

std::unique_ptr <O> p(new O());
...
p = nullptr;
演示赋裸指针:

reset()方法:

或者也可以使用“std::unique_ptr”的无入参版本的"reset()"方法:

p.reset();//让p变成空指向
演示reset方法:

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C->Cpp】由C迈向Cpp(3)

正文开始&#xff1a; 目录 &#xff08;一&#xff09;函数重载 &#xff08;1&#xff09;函数重载 &#xff08;2&#xff09;函数重载实现原理 &#xff08;二&#xff09; 引用 &#xff08;1&#xff09;引用 &#xff08;2&#xff09;语法 i &#xff0c;别名&am…

C#安装CommunityToolkit.Mvvm依赖

这里需要有一定C#基础&#xff0c; 首先找到右边的解决方案&#xff0c;右键依赖项 然后选择nuget管理 这里给大家扩展一下nuget的国内源&#xff08;https://nuget.cdn.azure.cn/v3/index.json&#xff09; 然后搜自己想要的依赖性&#xff0c;比如CommunityToolkit.Mvvm 再点…

Kibana:如何嵌入 Kibana 仪表板

作者&#xff1a;Carly Richmond 像我这样的前端工程师经常提出的要求是将 Kibana 等来源的现有仪表板嵌入到 JavaScript Web 应用程序中。 这是我必须多次执行的任务&#xff0c;因为我们希望快速部署用户生成的视图或允许用户控制给定的视图。 从我们从精彩的开发者社区收到的…

Git 初学

目录 一、需求的产生 二、版本控制系统理解 1. 认识版本控制系统 2. 版本控制系统分类 &#xff08;1&#xff09;集中式版本控制系统 缺点&#xff1a; &#xff08;2&#xff09;分布式版本控制系统 三、初识 git 四、git 的使用 例&#xff1a;将 “ OLED文件夹 ”…

每日一题 429.N叉树的层序遍历

429. N 叉树的层序遍历 描述&#xff1a; 给定一个 N 叉树&#xff0c;返回其节点值的层序遍历。&#xff08;即从左到右&#xff0c;逐层遍历&#xff09;。 树的序列化输入是用层序遍历&#xff0c;每组子节点都由 null 值分隔&#xff08;参见示例&#xff09;。 示例 1…

对待不合理需求,前端工程师如何优雅的say no!

曾经有位老板&#xff0c; 每次给前端提需求&#xff0c;前端都说实现不了&#xff0c;后来他搜索了一下&#xff0c;发现网上都有答案。他就在招聘要求上加了条&#xff1a;麻烦你在说不行的时候&#xff0c;搜索一下。 上面是一个段子&#xff0c;说的有点极端了&#xff0c;…

萨科微半导体宋仕强介绍说

萨科微半导体宋仕强介绍说&#xff0c;电源管理芯片是指在电子设备系统中&#xff0c;负责对电能的变换、分配、检测等进行管理的芯片&#xff0c;其性能和可靠性直接影响电子设备的工作效率和使用寿命&#xff0c;是电子设备中的关键器件。萨科微slkor&#xff08;www.slkormi…

vmware-17虚拟机安装教程及版本密钥(保姆级,包含图文讲解,不需注册账户)

文章目录 vmware安装教程一、下载vmware二、安装三、破解密匙 vmware安装教程 一、下载vmware 1.进入VMware官网&#xff1a;https://www.vmware.com/sg/products/workstation-pro.html 2.向下翻找到&#xff0c;如下界面并点击“现在安装” 3.稍事等待以下直到出现以下界面…

论文阅读_用模型模拟记忆过程

英文名称: A generative model of memory construction and consolidation 中文名称: 记忆构建和巩固的生成模型 文章: https://www.nature.com/articles/s41562-023-01799-z 代码: https://github.com/ellie-as/generative-memory 作者: Eleanor Spens, Neil Burgess&#xff…

什么软件可以监控电脑屏幕?

随着信息技术的飞速发展&#xff0c;电脑已成为企业日常运营不可或缺的工具。然而&#xff0c;这也带来了一系列的管理挑战&#xff0c;如何确保员工高效工作、避免信息泄露、监控不当行为等成为了企业迫切需要解决的问题。在这种背景下&#xff0c;电脑屏幕监控软件应运而生&a…

ubuntu22.04安装jenkins并配置

准备 更新系统 sudo apt update sudo apt upgrade环境准备 jdk 安装 sudo apt install openjdk-11-jdk验证 java -versiongit ubuntu配置git maven ubuntu配置maven 部署 添加 Jenkins 存储库 导入Jenkins存储库的GPG密钥 wget -q -O - https://pkg.jenkins.io/de…

自动更改由VSCode调试器创建的默认launch.json文件

File -> Preference -> Settings 修改下面的部分