【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(…...
使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...
20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...
Modbus RTU与Modbus TCP详解指南
目录 1. Modbus协议基础 1.1 什么是Modbus? 1.2 Modbus协议历史 1.3 Modbus协议族 1.4 Modbus通信模型 🎭 主从架构 🔄 请求响应模式 2. Modbus RTU详解 2.1 RTU是什么? 2.2 RTU物理层 🔌 连接方式 ⚡ 通信参数 2.3 RTU数据帧格式 📦 帧结构详解 🔍…...
