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

【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 结合使用 如上文所述&#xff0c;如果我们不初始化一个智能指针&#xff0c;那么它将会被初始化为一个空指针&#xff08;需要注意的是&#xff0c;智能指针与普通指针在此处有着非常明显的区别。如果只声明某个类型的普通指针&#xff0c;而不对它进…...

遥感数据集:FTW全球农田边界和对应影像数据,约160万田块边界及7万多个样本

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

马斯克的 AI 游戏工作室:人工智能与游戏产业的融合新纪元

近日&#xff0c;马斯克在 X 平台&#xff08;前身为 Twitter&#xff09;发文称&#xff0c;“太多游戏工作室被大型企业所拥有&#xff0c;xAI 将启动一个 AI 游戏工作室&#xff0c;让游戏再次变得精彩”。这一言论不仅展示了马斯克对游戏行业现状的不满&#xff0c;也揭示了…...

URDF(描述机器人模型)和SDF(Gazebo中用于描述仿真环境)

使用URDF&#xff08;Unified Robot Description Format&#xff09; URDF是ROS中用于描述机器人模型的XML格式文件。你可以使用XML文件定义机器人的几何形状、惯性参数、关节和链接等。 示例URDF文件&#xff08;my_robot.urdf&#xff09;&#xff1a; <?xml version&…...

力扣380:O(1)时间插入、删除和获取随机数

实现RandomizedSet 类&#xff1a; RandomizedSet() 初始化 RandomizedSet 对象bool insert(int val) 当元素 val 不存在时&#xff0c;向集合中插入该项&#xff0c;并返回 true &#xff1b;否则&#xff0c;返回 false 。bool remove(int val) 当元素 val 存在时&#xff0…...

【C++boost::asio网络编程】有关socket的创建和连接的笔记

socket的创建和连接 tcp客户端创建端点tcp服务端创建端点创建socket创建TCP 服务器端的 acceptor 套接字创建 acceptor 套接字并绑定客户端连接到服务器通过ip地址解析通过域名解析 服务端接收新连接 tcp客户端创建端点 int client_end_point() {std::string raw_ip_address …...

超级灵感:前端页面功能统一管理方案

前端页面功能统一管理方案 引言 我和朋友聊天想到一个灵感&#xff0c;关于支付状态机管理&#xff0c;这个类可以让我们知道具体上一个状态和下一个状态&#xff0c;这是由于那个事件触发改变&#xff0c;这个功能设计非常好&#xff01; 从而讨论出为什么我们不能把某一个…...

力扣第 77 题 组合

题目描述 给定两个整数 n 和 k&#xff0c;返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按任意顺序返回答案。 示例 示例 1 输入&#xff1a; n 4, k 2输出&#xff1a; [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]示例 2 输入&#xff1a; n 1, k …...

(超详细图文)PLSQL Developer 配置连接远程 Oracle 服务

1、下载配置文件 &#xff08;超详细图文详情&#xff09;Navicat 配置连接 Oracle-CSDN博客 将下载的文件解压到单独文件夹&#xff0c;如&#xff1a;D:\App\App_Java\Oracle\instantclient-basic-windows.x64-19.25.0.0.0dbru 2、配置 打开 PLSQL Developer&#xff0c;登…...

元器件选型与参数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集群-多路径&#xff08;multipathudev&#xff09;安装 配置虚拟存储 使用StarWind Management Console软件&#xff0c;配置存储 dggrid1: 1g*3 Dggrid2: 1g*3 Dgsystem: 5g*1 系统表空间&#xff0c;临时表空间&#xff0c;UNDO&#xff0c;参数文件…...

每日速记10道java面试题03

其他资料 每日速记10道java面试题01-CSDN博客 每日速记10道java面试题02-CSDN博客 目录 一、你使用过java的反射机制吗&#xff1f;如何应用反射&#xff1f; 二、什么是泛型&#xff1f;泛型的作用是什么&#xff1f; 三、java的泛型擦除是什么&#xff1f; 四、Java 中…...

Vue 3 的双向绑定原理

Vue 3 的双向绑定原理是基于 响应式系统 和 数据劫持 技术来实现的。在 Vue 3 中&#xff0c;双向绑定通常是通过 v-model 指令来完成的&#xff0c;它本质上是数据的双向同步&#xff1a;当数据改变时&#xff0c;视图自动更新&#xff0c;反之&#xff0c;视图的修改也会更新…...

如何使用 Chrome 无痕浏览模式访问网站?

无痕浏览&#xff08;Incognito Mode&#xff09;是 Google Chrome 浏览器提供的一种隐私保护功能&#xff0c;它允许用户在一个独立的会话中浏览网页&#xff0c;而不会记录用户的浏览历史、下载历史、表单数据等。这对于希望保护个人隐私或进行临时性匿名浏览的用户来说非常有…...

Idea 2024.3 突然出现点击run 运行没有反应,且没有任何提示。

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

【小白学机器学习36】关于独立概率,联合概率,交叉概率,交叉概率和,总概率等 概念辨析的例子

目录 1 先说结论 2 联合概率 3 边缘概率 4 (行/列)边缘概率的和 总概率1 5 条件概率 5.1 条件概率的除法公式 5.2 条件概率和联合概率区别 1 先说结论 关于独立概率&#xff0c;联合概率&#xff0c;交叉概率&#xff0c;交叉概率和&#xff0c;总概率 类型含义 …...

Spring Boot 项目——分层架构

在创建一个 Spring Boot 项目时&#xff0c;为了提高代码的可维护性、可扩展性和清晰度&#xff0c;通常会按照一定的分层架构进行设计。常见的分层架构包括以下几层&#xff1a; 1. Controller 层&#xff08;Web 层&#xff09; 作用&#xff1a;接收用户请求&#xff0c;并…...

wordpress网站首页底部栏显示网站备案信息

一、页脚文件footer.php 例如&#xff0c;wordpress主题使用的是simple-life主题&#xff0c;服务器IP为192.168.68.89,在wordpress主题文件中有个页脚文件footer.php&#xff0c;这是一个包含网站页脚代码的文件。 footer.php 路径如下&#xff1a; /www/wwwroot/192.168.68…...

python面向对象编程练习

学生成绩管理系统 定义一个Student类&#xff0c;包括属性&#xff08;姓名、成绩&#xff09;和方法&#xff08;设置成绩、获取成绩、计算平均成绩&#xff09;。 实例化多个学生对象并调用方法。 功能说明&#xff1a; Student 类&#xff1a; init(self, name)&#xff1a;…...

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 是苹果推出的一种多平台二进制分发格式&#xff0c;可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

【kafka】Golang实现分布式Masscan任务调度系统

要求&#xff1a; 输出两个程序&#xff0c;一个命令行程序&#xff08;命令行参数用flag&#xff09;和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽&#xff0c;然后将消息推送到kafka里面。 服务端程序&#xff1a; 从kafka消费者接收…...

使用分级同态加密防御梯度泄漏

抽象 联邦学习 &#xff08;FL&#xff09; 支持跨分布式客户端进行协作模型训练&#xff0c;而无需共享原始数据&#xff0c;这使其成为在互联和自动驾驶汽车 &#xff08;CAV&#xff09; 等领域保护隐私的机器学习的一种很有前途的方法。然而&#xff0c;最近的研究表明&…...

基于Docker Compose部署Java微服务项目

一. 创建根项目 根项目&#xff08;父项目&#xff09;主要用于依赖管理 一些需要注意的点&#xff1a; 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件&#xff0c;否则打包时会出问题 <?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中&#xff0c;给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加&#xff0c;包括必要的代码示例和详细解释。 环境准备 在开始之前&#xff0c;需要安装以下Python库&#xff1a;…...

九天毕昇深度学习平台 | 如何安装库?

pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子&#xff1a; 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...

GitFlow 工作模式(详解)

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

【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)

前言&#xff1a; 双亲委派机制对于面试这块来说非常重要&#xff0c;在实际开发中也是经常遇见需要打破双亲委派的需求&#xff0c;今天我们一起来探索一下什么是双亲委派机制&#xff0c;在此之前我们先介绍一下类的加载器。 目录 ​编辑 前言&#xff1a; 类加载器 1. …...

comfyui 工作流中 图生视频 如何增加视频的长度到5秒

comfyUI 工作流怎么可以生成更长的视频。除了硬件显存要求之外还有别的方法吗&#xff1f; 在ComfyUI中实现图生视频并延长到5秒&#xff0c;需要结合多个扩展和技巧。以下是完整解决方案&#xff1a; 核心工作流配置&#xff08;24fps下5秒120帧&#xff09; #mermaid-svg-yP…...