【C++】动态内存与智能指针——shared_ptr 和 new 结合使用
12.1.3 shared_ptr 和 new 结合使用
如上文所述,如果我们不初始化一个智能指针,那么它将会被初始化为一个空指针(需要注意的是,智能指针与普通指针在此处有着非常明显的区别。如果只声明某个类型的普通指针,而不对它进行初始化,那么这将会是危险的,因为不经初始化的普通指针是空悬指针,它所指向的地址是未知的。而不经初始化的智能指针是一个空指针,它是安全的)。还可以用 new 返回的指针来初始化智能指针:
shared_ptr<double> p1; // shared_ptr 可以指向一个 double
shared_ptr<int> p2(new int(42)); // p2 指向一个值为 42 的 int
接受指针参数的智能指针构造函数是 explicit 的。因此不能将一个内置隐式指针转换为一个智能指针,必须使用直接初始化形式来初始化一个普通指针:
shared_ptr<int> p1 = new int(1024); // 错误❌: 必须使用直接初始化形式
shared_ptr<int> p2(new int(1024)); // 正确👌: 使用了直接初始化形式
p1 的初始化隐式地要求编译器用一个 new 返回的 int* 来创建一个 shared_ptr。由于我们不能进行内置指针到智能指针的隐式转换,因此这条初始化语句是错误的。出于相同的原因,一个返回 shared_ptr 的函数不能在其返回语句中隐式转换一个普通指针:
shared_ptr<int> clone(int p) {return new int(p); // 错误❌: 隐式转换为 shared_ptr<int>
}
(这一部分很好理解,C++ 智能指针的规则就是不能将普通指针隐式地转换为智能指针,由于 new 的返回值是某个类型的普通指针,普通指针不能隐式转为智能指针,因此使用 new … 对智能指针进行赋值是非法的)
我们必须将 shared_ptr 显式绑定到一个想要返回的指针上:
shared_ptr<int> clone(int p) {return shared_ptr<int>(new int(p)); // 正确👌: 显式地使用 int* 来创建 shared_ptr<int>
}
默认情况下,一个用来初始化智能指针的普通指针必须指向动态内存,因为智能指针默认使用 delete 释放它所关联的对象。
可以将智能指针绑定到一个指向其它类型的资源的指针上,但是为了这样做,我们必须提供自己的操作来替代 delete。
不要混合使用普通指针和智能指针 … …
shared_ptr 可以协调对象的析构,但这仅限于其自身的拷贝(也是 shared_ptr)之间。这也是 C++ Primer 推荐使用 make_shared 而不是 new 的原因。
这样,我们就可以在分配对象的同时将 shared_ptr 与之绑定,从而避免了无意中将同一块内存绑定到多个独立创建的 shared_ptr 上。
考虑下面对 shared_ptr 进行操作的函数:
// 在函数调用时 shared_ptr 被创建并初始化
void process(shared_ptr<int> ptr) {// 使用 ptr
} // ptr 离开作用域, 被销毁
process 的参数是传值方式调用的,因此实参会被拷贝到 ptr 中。拷贝一个 shared_ptr 会递增其引用计数,因此,在 process 运行过程中,引用计数值至少为 2。当 process 结束时,ptr 的引用计数递减,但不会变为 0。因此,当局部变量 ptr 被销毁时,ptr 指向的内存不会被释放。
使用此函数的正确方法是传递给它一个 shared_ptr:
shared_ptr<int> p(new int(42)); // 引用计数为 1
process(p); // 拷贝 p 会递增它的引用计数; 在 process 中引用计数值为 2
int i = *p; // 正确: 引用计数值为 1
虽然不能传递给 process 一个内置指针,但可以传递给它一个(临时的)shared_ptr,这个 shared_ptr 是用一个内置指针显式构造的。但这样做可能会导致错误:
int *x(new int(1024)); // 危险: x 是一个普通指针, 不是一个智能指针;
process(x); // 错误❌: 不能将 int* 转换为一个 shared_ptr<int>;
process(shared_ptr<int>(x)); // 合法的, 但内存会被释放;
int j = *x; // 未定义的, x 是一个空悬指针;
在上面的调用中,第一行建立一个指向动态内存的 x 指针,它是一个 int 类型的指针,指向的值为 1024,并且它是一个普通指针。第二行是非法的,因为 shared_ptr 和 普通指针之间没有隐式转换。第三行是正确的,因为第三行的实参中显式地将普通指针转为智能指针,但是智能指针所指向的地址会在函数调用结束时被释放。因此,第四行是危险的,因为 x 所指向的内存已经被释放(被释放的过程在 C++ Primer 当中的解释不是很详尽,我的理解是这样的:当第三行对 process 进行调用时,x 被显式地转换为智能指针,指向 x 所指地址的引用计数被置为 1。当函数调用结束时,x 所指地址的引用计数被置为 0,该部分的内存随之被释放。此时,由于函数调用外的 x 是一个普通指针,它所指向的地址仍然是最初的地址,而最初地址的内存在 process 函数调用后被释放,所以此时 x 指向的地址被释放,x 变为一个危险的空悬指针)。
当将一个 shared_ptr 绑定到一个普通指针时,我们就将内存的管理责任交给了这个 shared_ptr。一旦这样做了,我们就不能再使用内置指针来访问 shared_ptr 所指向的内存了。
使用一个内置指针来访问一个智能指针所负责的对象是很危险的,因为我们无法知道对象何时被销毁。
… … 也不要使用 get 初始化另一个智能指针或为智能指针赋值
智能指针类型定义了一个名为 get 的函数,它返回一个内置指针,指向智能指针关联的对象。此函数是为了这样一种情况而设计的:我们需要向一个不能使用智能指针的代码传递一个内置指针。使用 get 返回的指针的代码不能 delete 此指针。
虽然编译器不会给出错误信息,但将另一个智能指针也绑定到 get 返回的指针上是错误的:
shared_ptr<int> p(new int(42)); // 引用计数为 1
int *q = p.get(); // 正确👌: 但使用 q 时要注意, 不要让它管理的指针被释放
{ // 新程序块shared_ptr<int>(q); // 未定义: 两个独立的 shared_ptr 指向相同的内存
} // 程序块结束, q 被销毁, 它指向的内存被释放
int foo = *p; // 未定义: p 指向的内存已经被释放;
在本例中,p 和 q 指向相同的内存。由于它们是相互独立创建的,因此各自的引用计数都是 1。当 q 所在的程序块结束时,q 被销毁,这会导致 q 指向的内存被释放。从而 p 变成一个空悬指针。
get 用来将指针的访问权限传递给代码,你只有在确定代码不会 delete 指针的情况下,才能使用 get。
其它 shared_ptr 操作
shared_ptr 还定义了一些其它操作。可以用 reset 来将一个新的指针赋予一个 shared_ptr:
p = new int(1024); // 错误❌: 不能将一个指针赋予 shared_ptr
p.reset(new int(1024)); // 正确👌: p 指向一个新对象
与赋值类似,reset 会更新引用计数,如果需要的话,会释放 p 指向的对象。reset 成员常与 unique 一起使用,来控制多个 shared_ptr 共享的对象。
相关文章:

【C++】动态内存与智能指针——shared_ptr 和 new 结合使用
12.1.3 shared_ptr 和 new 结合使用 如上文所述,如果我们不初始化一个智能指针,那么它将会被初始化为一个空指针(需要注意的是,智能指针与普通指针在此处有着非常明显的区别。如果只声明某个类型的普通指针,而不对它进…...

遥感数据集:FTW全球农田边界和对应影像数据,约160万田块边界及7万多个样本
Fields of The World (FTW) 是一个面向农业田地边界实例分割的基准数据集,旨在推动机器学习模型的发展,满足全球农业监测对高精度、可扩展的田地边界数据的需求。该数据集由kerner-lab提供,于2024年8月28日发布,主要特征包括&…...

马斯克的 AI 游戏工作室:人工智能与游戏产业的融合新纪元
近日,马斯克在 X 平台(前身为 Twitter)发文称,“太多游戏工作室被大型企业所拥有,xAI 将启动一个 AI 游戏工作室,让游戏再次变得精彩”。这一言论不仅展示了马斯克对游戏行业现状的不满,也揭示了…...
URDF(描述机器人模型)和SDF(Gazebo中用于描述仿真环境)
使用URDF(Unified Robot Description Format) URDF是ROS中用于描述机器人模型的XML格式文件。你可以使用XML文件定义机器人的几何形状、惯性参数、关节和链接等。 示例URDF文件(my_robot.urdf): <?xml version&…...
力扣380:O(1)时间插入、删除和获取随机数
实现RandomizedSet 类: RandomizedSet() 初始化 RandomizedSet 对象bool insert(int val) 当元素 val 不存在时,向集合中插入该项,并返回 true ;否则,返回 false 。bool remove(int val) 当元素 val 存在时࿰…...
【C++boost::asio网络编程】有关socket的创建和连接的笔记
socket的创建和连接 tcp客户端创建端点tcp服务端创建端点创建socket创建TCP 服务器端的 acceptor 套接字创建 acceptor 套接字并绑定客户端连接到服务器通过ip地址解析通过域名解析 服务端接收新连接 tcp客户端创建端点 int client_end_point() {std::string raw_ip_address …...

超级灵感:前端页面功能统一管理方案
前端页面功能统一管理方案 引言 我和朋友聊天想到一个灵感,关于支付状态机管理,这个类可以让我们知道具体上一个状态和下一个状态,这是由于那个事件触发改变,这个功能设计非常好! 从而讨论出为什么我们不能把某一个…...
力扣第 77 题 组合
题目描述 给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按任意顺序返回答案。 示例 示例 1 输入: n 4, k 2输出: [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]示例 2 输入: n 1, k …...

(超详细图文)PLSQL Developer 配置连接远程 Oracle 服务
1、下载配置文件 (超详细图文详情)Navicat 配置连接 Oracle-CSDN博客 将下载的文件解压到单独文件夹,如:D:\App\App_Java\Oracle\instantclient-basic-windows.x64-19.25.0.0.0dbru 2、配置 打开 PLSQL Developer,登…...

元器件选型与参数13 电源的分类-线性电源参数 RT9013 AMS1117 PCB布局布线
目录 一、线性电源 1、重要参数 2、线性电源效率一定低吗 3、线性电源并联扩流 4、常见电路 RT9013-LDO AMS1117-xx-LDO 5、布局布线 6、外置输入与电池供电 7、单片机控制其他模组供电实现低功耗 二、开关电源与线性电源配合 1、高效率与低噪声 DC-DC电源大致分为…...

RHEL7+Oracle11.2 RAC集群-多路径(multipath+udev)安装步骤
RHEL7Oracle11.2RAC集群-多路径(multipathudev)安装 配置虚拟存储 使用StarWind Management Console软件,配置存储 dggrid1: 1g*3 Dggrid2: 1g*3 Dgsystem: 5g*1 系统表空间,临时表空间,UNDO,参数文件…...
每日速记10道java面试题03
其他资料 每日速记10道java面试题01-CSDN博客 每日速记10道java面试题02-CSDN博客 目录 一、你使用过java的反射机制吗?如何应用反射? 二、什么是泛型?泛型的作用是什么? 三、java的泛型擦除是什么? 四、Java 中…...
Vue 3 的双向绑定原理
Vue 3 的双向绑定原理是基于 响应式系统 和 数据劫持 技术来实现的。在 Vue 3 中,双向绑定通常是通过 v-model 指令来完成的,它本质上是数据的双向同步:当数据改变时,视图自动更新,反之,视图的修改也会更新…...

如何使用 Chrome 无痕浏览模式访问网站?
无痕浏览(Incognito Mode)是 Google Chrome 浏览器提供的一种隐私保护功能,它允许用户在一个独立的会话中浏览网页,而不会记录用户的浏览历史、下载历史、表单数据等。这对于希望保护个人隐私或进行临时性匿名浏览的用户来说非常有…...

Idea 2024.3 突然出现点击run 运行没有反应,且没有任何提示。
写这篇文章的目的是为了提供一个新的解决思路,因为存在同病不同原因。 如果你进行了1. 检查运行配置 (Run Configuration) 2. 清理和重建项目 3. 清除缓存并重启 IDEA 4.排除kotlin 5.重装idea等等操作之后仍然没有解决,可以试着按一下步骤进行解决。 检…...

【小白学机器学习36】关于独立概率,联合概率,交叉概率,交叉概率和,总概率等 概念辨析的例子
目录 1 先说结论 2 联合概率 3 边缘概率 4 (行/列)边缘概率的和 总概率1 5 条件概率 5.1 条件概率的除法公式 5.2 条件概率和联合概率区别 1 先说结论 关于独立概率,联合概率,交叉概率,交叉概率和,总概率 类型含义 …...
Spring Boot 项目——分层架构
在创建一个 Spring Boot 项目时,为了提高代码的可维护性、可扩展性和清晰度,通常会按照一定的分层架构进行设计。常见的分层架构包括以下几层: 1. Controller 层(Web 层) 作用:接收用户请求,并…...

wordpress网站首页底部栏显示网站备案信息
一、页脚文件footer.php 例如,wordpress主题使用的是simple-life主题,服务器IP为192.168.68.89,在wordpress主题文件中有个页脚文件footer.php,这是一个包含网站页脚代码的文件。 footer.php 路径如下: /www/wwwroot/192.168.68…...
python面向对象编程练习
学生成绩管理系统 定义一个Student类,包括属性(姓名、成绩)和方法(设置成绩、获取成绩、计算平均成绩)。 实例化多个学生对象并调用方法。 功能说明: Student 类: init(self, name):…...

OpenCV_Code_LOG
孔洞填充 void fillHole(const Mat srcBw, Mat &dstBw) {Size m_Size srcBw.size();Mat TempMat::zeros(m_Size.height2,m_Size.width2,srcBw.type());//延展图像srcBw.copyTo(Temp(Range(1, m_Size.height 1), Range(1, m_Size.width 1)));cv::floodFill(Temp, Point(…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...

使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...

基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...

九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...

GitFlow 工作模式(详解)
今天再学项目的过程中遇到使用gitflow模式管理代码,因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存,无论是github还是gittee,都是一种基于git去保存代码的形式,这样保存代码…...

【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)
前言: 双亲委派机制对于面试这块来说非常重要,在实际开发中也是经常遇见需要打破双亲委派的需求,今天我们一起来探索一下什么是双亲委派机制,在此之前我们先介绍一下类的加载器。 目录 编辑 前言: 类加载器 1. …...
comfyui 工作流中 图生视频 如何增加视频的长度到5秒
comfyUI 工作流怎么可以生成更长的视频。除了硬件显存要求之外还有别的方法吗? 在ComfyUI中实现图生视频并延长到5秒,需要结合多个扩展和技巧。以下是完整解决方案: 核心工作流配置(24fps下5秒120帧) #mermaid-svg-yP…...