【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(…...
力扣第 74 题是 搜索二维矩阵
题目描述 给定一个 m x n 的矩阵 matrix 和一个目标值 target,请你编写一个函数来判断目标值 target 是否在矩阵中。 每行的元素按升序排列。每列的元素按升序排列。 示例 1 输入: matrix [[1, 4, 7, 11],[2, 5, 8, 12],[3, 6, 9, 16],[10, 13, 14…...
[极客大挑战 2019]BabySQL--详细解析
信息搜集 进入界面: 输入用户名为admin,密码随便输一个: 发现是GET传参,有username和password两个传参点。 我们测试一下password点位能不能注入: 单引号闭合报错,根据报错信息,我们可以判断…...
实现Linux平台自定义协议族
一 简介 我们常常在Linux系统中编写socket接收TCP/UDP协议数据,大家有没有想过它怎么实现的,如果我们要实现socket接收自定义的协议数据又该怎么做呢?带着这个疑问,我们一起往下看吧~~ 二 Linux内核函数简介 在Linux系统中要想…...
RL78/G15 Fast Prototyping Board Arduino IDE 平台开发过程
这是一篇基于RL78/G15 Fast Prototyping Board的Arduino IDE开发记录 RL78/G15 Fast Prototyping Board硬件简介(背景)基础测试(方法说明/操作说明)开发环境搭建(方法说明/操作说明代码结果)Arduino IDE RL…...
YOLOv11 NCNN安卓部署
YOLOv11 NCNN安卓部署 前言 yolov11 NCNN安卓部署 目前的帧率可以稳定在20帧左右,下面是这个项目的github地址:https://github.com/gaoxumustwin/ncnn-android-yolov11 上面的检测精度很低时因为这个模型只训练了5个epoch,使用3090训练一个…...
对载入的3dtiles进行旋转、平移和缩放变换。
使用 params: {tx: 129.75845, //模型中心X轴坐标(经度,单位:十进制度)//小左ty: 46.6839, //模型中心Y轴坐标(纬度,单位:十进制度)//小下tz: 28, //模型中心Z轴坐标(高…...
Rust个人认为将抢占C和C++市场,逐渐成为主流的开发语言
本人使用C开发8年、C#开发15年、中间使用JAVA开发过项目、后期在学习过程中发现了Rust语言说它是最安全的语言,能够解决C、C的痛点、于是抽出一部分时间网上买书,看网上资料进行学习,这一学习起来发现和其它语言比较起来,在编码的…...
在openEuler中使用top命令
在openEuler中使用top命令 概述 top 命令是Linux系统中最常用的实时性能监控工具之一,允许用户查看系统的整体状态,包括CPU使用率、内存使用情况、运行中的进程等。本文档将详细介绍如何在openEuler操作系统中有效利用top命令进行系统监控。 启动top命令 打开终端并输入t…...
探索文件系统,Python os库是你的瑞士军刀
文章目录 探索文件系统,Python os库是你的瑞士军刀第一部分:背景介绍第二部分:os库是什么?第三部分:如何安装os库?第四部分:简单库函数使用方法1. 获取当前工作目录2. 改变当前工作目录3. 列出目…...
【小白学机器学习41】如何从正态分布的总体中去抽样? 获得指定正态分布的样本的2种方法
目录 1 目标:使用2种方法,去从正态分布的总体中去抽样,获得样本 1.1 step1: 首先,逻辑上需要先有符合正态分布的总体population 1.2 从总体中取得样本,模拟抽样的过程 2 从正态分布抽样的方法1 3 从正态分布抽样…...
找别人做网站注意事项/如何做好网络营销推广
返回:贺老师课程教学链接 【项目1-程序的多文件组织】请将下面的程序中的代码分别保存在两个文件中,利用多文件的方式组织。 * main.c(也可以是别的名字) #include <stdio.h> int max(int x,int y); int min(int x,int y…...
python web网站开发/媒体发稿平台
C程序设计实验报告 实验项目: 1、利用复化梯形公式计算定积分2、计算Ackerman函数3、编写计算x的y次幂的递归函数getpower(int x,int y),并在主程序中实现输入输出4、编写计算学生年龄的递归函数5、编写递归函数实现Ackman函数 姓名:张时锋 …...
如何查看网站开发的语言/西安百度公司地址介绍
当碰到较小自动时间步时,我们应采取哪些策略来提高仿真效率?本文我们列举了一些示例,并讨论了如何通过调整求解器设置来应对较小的时间步。在求解器日志中追踪时间步和离散阶次当使用 BDF 时间步进方法检查与时间有关的仿真求解器日志时&…...
网站个人主页怎么做/百度seo培训要多少钱
第四章课后作业(6—27)6.试按下列要求分别编制程序段。(1)把标志寄存器中符号位SF置“1”。(2)寄存器AL中高、低四位互换。(3)由寄存器AX、BX组成一个32位带符号数(AX中存放高16位),试求这个数的负数。(4)现有三个字节存储单元A、B、C,在不使用ADD和ADC指…...
淘宝网站优化实例/什么是电商
先上软件效果图 代码如下1.根据Url地址得到网页的html源码 1 public static string GetWebContent(string Url)2 {3 string strResult "";4 try5 {6 HttpWebRequest request (HttpWebRequest)WebReq…...
php做动态网站/seo搜索引擎优化技术
SQL Server 数据库启动过程,以及启动不起来的各种问题的分析及解决技巧参考文章: (1)SQL Server 数据库启动过程,以及启动不起来的各种问题的分析及解决技巧 (2)https://www.cnblogs.com/VicL…...