C++智能指针(二)——weak_ptr初探
文章目录
- 1. shared_ptr 存在的问题
- 2. 使用weak_ptr
- 2.1 初始化 weak_ptr
- 2.2 访问数据
- 3. 附录
- 4. 参考文献
1. shared_ptr 存在的问题
与 shared_ptr
的引入要解决普通指针存在的一些问题一样,weak_ptr
的引入,也是因为 shared_ptr
本身在某些情况下,存在一些问题或有一些不完善的地方,考虑以下两个场景:
- 循环引用(cyclic references)。如果两个对象使用
shared_ptrs
互相引用,那么就算将两个对象指针设为nullptr,此时理应释放资源,但由于内部的循环引用,此时 shared_ptrs 的use_count() = 1
,导致并不会释放资源
下面为循环引用的一个具体示例代码:
#include <iostream>
#include <string>
#include <vector>
#include <memory>
using namespace std;
class Person {
public:
string name;
shared_ptr<Person> mother;
shared_ptr<Person> father;
vector<shared_ptr<Person>> kids;
Person (const string& n,
shared_ptr<Person> m = nullptr,
shared_ptr<Person> f = nullptr)
: name(n), mother(m), father(f) {
}
~Person() {
cout << "delete " << name << endl;
}
};
shared_ptr<Person> initFamily (const string& name)
{
shared_ptr<Person> mom(new Person(name+"’s mom"));
shared_ptr<Person> dad(new Person(name+"’s dad"));
shared_ptr<Person> kid(new Person(name,mom,dad));
mom->kids.push_back(kid);
dad->kids.push_back(kid);
return kid;
}
int main()
{
shared_ptr<Person> p = initFamily("nico");
cout << "nico’s family exists" << endl;
cout << "- nico is shared " << p.use_count() << " times" << endl;
cout << "- name of 1st kid of nico’s mom: "
<< p->mother->kids[0]->name << endl;
p = initFamily("jim");
cout << "jim’s family exists" << endl;
}
首先,initFamily()
创建了三个Person对象:mon,dad 和 kid。kid 使用了 mom 和 dad 的共享指针进行创建。mom 和 dad 也将 kid 共享指针插入到 vector
中,最后将 kid 指针返回给 p,initFamily()
调用完结果如下图所示。
kid 有指向 mom 和 dad 的指针,mom 和 dad 中也有指向 kid 的指针,此时循环引用就产生了。因此这里 p 的 use_count=3
,所以当赋值一个新的Person给p或者让p为nullptr,或者在 main() 末尾离开了 p 的作用域 —— 没有 Person 对象会被释放,因为每个至少有一个指针指向,因此输出 delete name
永远不会调用,实际输出如下:
nico’s family exists
- nico shared 3 times
- name of 1st kid of nicos mom: nico
jim’s family exists
- 如果只是想共享而不是想拥有对象。即一个指针的生命周期要长于指向对象的生命周期。此时使用
shared_ptrs
会导致无法释放资源,使用普通指针存在访问释放资源的风险,后续对weak_ptr
使用的讲解中进一步说明。
2. 使用weak_ptr
鉴于上面 shared_ptr
存在的问题,C++11 提供了 weak_ptr
类,允许共享对象,但并不实际拥有对象,这个类需要传入一个共享指针来创建。当最后一个共享指针失去对象所有权(要释放空间与资源了),共享对象的 weak_ptr
自动设为空(本来就没有对象的所有权,自然也不负责对于空间与资源的释放) 。
我们使用 weak_ptr
改写上面的代码:
class Person {
public:
string name;
shared_ptr<Person> mother;
shared_ptr<Person> father;
vector<weak_ptr<Person>> kids; // weak pointer !!!
Person (const string& n,
shared_ptr<Person> m = nullptr,
shared_ptr<Person> f = nullptr)
: name(n), mother(m), father(f) {
}
~Person() {
cout << "delete " << name << endl;
}
};
通过使用 weak_ptr
打破共享指针的循环引用,只有 kid 指向父母的指针使用共享指针,父母指向 kid 的指针使用(下图中的虚线)
这样 p 的 use_coun=1
,所以 p 删除时,会释放对应的内存和资源。程序输出如下:
nico’s family exists
- nico shared 1 times
- name of 1st kid of nicos mom: nico
delete nico
delete nico’s dad
delete nico’s mom
jim’s family exists
delete jim
delete jim’s dad
delete jim’s mom
下面详细讲解 weak_ptr
的使用
2.1 初始化 weak_ptr
因为 weak_ptr
只能使用 shared_ptr
初始化,所以 weak_ptr
只提供了默认构造函数、拷贝构造函数以及传入 shared_ptr
的构造函数,因为不是显式构造函数,所以可以在 vector
中直接插入共享指针(隐式转换):
mom->kids.push_back(kid);
dad->kids.push_back(kid);
2.2 访问数据
之前使用 shared_ptr
访问 vector 中共享指针指向的数据使用以下语法:
p->mother->kids[0]->name
而对于 weak_ptr
则要使用如下语法:
p->mother->kids[0].lock()->name
lock()
获取共享指针,。如果在 lock 获取共享指针时,资源已经被释放了,则返回空的 shared_ptr
。
此时,再调用操作符 *
或 ->
都会产生未定义行为。
因此,最好在获取共享指针前,首先对资源是否释放进行检查,有如下 3 种方法:
- 调用
expired()
方法,如果weak_ptr
不再共享一个对象则返回 true。这与检查use_count()
是否等于 0 是等价的,但可能运行速度更快。 - 可以显式将
weak_ptr
使用对应构造函数转换为shared_ptr
。如果此时没有合法的引用对象,则这个构造函数抛出一个bad_weak_ptr
异常。 这是一个派生自std::exception
的一个异常,what()
返回bad_weak_ptr
(每个设备上实现有所差异)。 - 可以调用
use_count()
查询关联对象所有者的数量。如果返回值是 0,这将不会再有合法对象。这个方法最好只是在debug时使用,因为效率不高。
三种方法的具体代码如下:
try {
shared_ptr<string> sp(new string("hi")); // create shared pointer
weak_ptr<string> wp = sp; // create weak pointer out of it
sp.reset(); // release object of shared pointer
cout << wp.use_count() << endl; // prints: 0
cout << boolalpha << wp.expired() << endl; // prints: true
shared_ptr<string> p(wp); // throws std::bad_weak_ptr
}
catch (const std::exception& e) {
cerr << "exception: " << e.what() << endl; // prints: bad_weak_ptr
}
3. 附录
A. weak_ptr
操作列表
4. 参考文献
《The C++ Standard Library》A Tutorial and Reference, Second Edition, Nicolai M. Josuttis.
相关文章:

C++智能指针(二)——weak_ptr初探
文章目录 1. shared_ptr 存在的问题2. 使用weak_ptr2.1 初始化 weak_ptr2.2 访问数据 3. 附录4. 参考文献 1. shared_ptr 存在的问题 与 shared_ptr 的引入要解决普通指针存在的一些问题一样,weak_ptr 的引入,也是因为 shared_ptr 本身在某些情况下&…...

540 - Team Queue (UVA)
题目链接如下: Online Judge 对比刘汝佳的代码,我没有用queue来排整个队伍,因为那样的话遍历整个队伍太麻烦,vector比较方便。但vector删除元素比较耗时,所以就不删了,仅仅用pivot来指代目前队伍的开始。…...

投资组合之如何估值
文章目录 如何估值一、PE估值法1、PE估值法的定义2、参考标准(1)常规标准:25倍合理市盈率。(2)同行业对比。(3)跟历史市盈率相比。 3、PE估值法的适用范围4、PE估值法的优势5、PE估值法的劣势&a…...

2024届通信工程保研经验分享(预推免入营即offer)
2024届通信工程保研经验分享(预推免入营即offer) BackGround夏令营情况:预推免情况: BackGround 本科院校:末九 专业:通信工程 rank:3/123(预推免绩点排名)࿰…...

L2-025 分而治之 - java
L2-025 分而治之 时间限制 600 ms 内存限制 64 MB 题目描述: 分而治之,各个击破是兵家常用的策略之一。在战争中,我们希望首先攻下敌方的部分城市,使其剩余的城市变成孤立无援,然后再分头各个击破。为此参谋部提供了若…...

Python+高光谱数据预处理-机器学习-深度学习-图像分类-参数回归
涵盖高光谱遥感数据处理的基础、python开发基础、机器学习和应用实践。重点解释高光谱数据处理所涉及的基本概念和理论,旨在帮助学员深入理解科学原理。结合Python编程工具,专注于解决高光谱数据读取、数据预处理、高光谱数据机器学习等技术难题…...

免费 AI 编程助手 Amazon CodeWhisperer 体验
文章作者:文章作者:米菲爸爸 2022 年 6 月 23 亚马逊云科技就已经推出了 Amazon CodeWhisperer(预览版)。经过不到一年的测试和 AIGC的飓风在 2023 年 4 月 18 日实时 AI 编程助手 Amazon CodeWhisperer正式可用 Amazon CodeWhis…...

【Linux】从零开始学习Linux基本指令(一)
🚩纸上得来终觉浅, 绝知此事要躬行。 🌟主页:June-Frost 🚀专栏:Linux入门 🔥该文章主要了解Linux操作系统下的基本指令。 目录: ⌛️指令的理解⏳目录和文件的理解⏳一些常见指令✉…...

Java GC 算法
一、概述 理解Java虚拟机垃圾回收机制的底层原理,是成为一个高级Java开发者的基本功。本文从底层的垃圾回收算法开始,着重去阐释不同垃圾回收器在算法设计和实现时的一些技术细节,去探索「why」这一部分,通过对比不同的垃圾回收算…...

vue3 v-html中使用v-viewer
安装:npm install v-viewernext 在main.js中配置 import “viewerjs/dist/viewer.css”; import Viewer from “v-viewer”; app.use(Viewer, { Options: { inline: true, //默认值:false。启用内联模式。 button: true, //在查看器的右上角显示按钮。 …...

Leetcode算法解析——查找总价格为目标值的两个商品
1. 题目链接:LCR 179. 查找总价格为目标值的两个商品 2. 题目描述: 商品价格按照升序记录于数组 price。请在购物车中找到两个商品的价格总和刚好是 target。若存在多种情况,返回任一结果即可。 示例 1: 输入:price …...

unity游戏开发引擎unity3D开发
Unity(也被称为Unity3D)是一款强大的跨平台游戏引擎,用于开发2D和3D游戏,以及其他交互式应用程序。以下是Unity游戏开发的一般步骤: 安装和设置Unity: 首先,您需要下载并安装Unity。确保选择适…...

iptables
目录 iptables 匹配规则:由上到下依次匹配,一旦匹配不再匹配 参数 知识点 REJECT与DROP REJECT与DROP的区别 当使用的时REJECT时,客户端访问迅速返回的值是拒绝连接 当使用的是DROP时,返回的时连接超时 REJECT与drop适用…...

竞赛 深度学习LSTM新冠数据预测
文章目录 0 前言1 课题简介2 预测算法2.1 Logistic回归模型2.2 基于动力学SEIR模型改进的SEITR模型2.3 LSTM神经网络模型 3 预测效果3.1 Logistic回归模型3.2 SEITR模型3.3 LSTM神经网络模型 4 结论5 最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 …...

Spark入门
目录 Spark入门: 概述历史概述SparkCore:RDDSparkSQL:SparkStreamingSpark内核调优 Spark概述 回顾: Hadoop HDFS存储 MR分析计算 YARN调度 Hadoop的MR计算中的shuffle需要落盘,速度不够快。 Spark是一种基于内存的分析计算引擎。 历史…...

react–antd 实现TreeSelect树形选择组件,实现点开一层调一次接口
效果图: 注意: 当选择“否”,开始调接口,不要把点击调接口写在TreeSelect组件上,这样会导致问题出现,没有层级了 部分代码:...

android 固定进度环形刷新效果
android 固定进度无限旋转的环形效果 效果图 效果视频: Record_2023-10-13-17-17-19[1] Activity 中使用 val rotation: ObjectAnimator ObjectAnimator.ofFloat(progressBar, "rotation", 0f, 360f) rotation.duration 000 // 旋转持续时间为2秒 rot…...

python jieba 词性标注 中文词性分类 nlp jieba.posseg
参考:https://blog.csdn.net/yellow_python/article/details/83991967 from jieba.posseg import dt dt.word_tag_tab[好看] >>> vflag_en2cn { ‘a’: ‘形容词’, ‘ad’: ‘副形词’, ‘ag’: ‘形语素’, ‘an’: ‘名形词’, ‘b’: ‘区别词’, ‘…...

LeetCode 每日一题 2023/10/9-2023/10/15
记录了初步解题思路 以及本地实现代码;并不一定为最优 也希望大家能一起探讨 一起进步 目录 10/9 2578. 最小和分割10/10 2731. 移动机器人10/11 2512. 奖励最顶尖的 K 名学生10/12 2562. 找出数组的串联值10/13 1488. 避免洪水泛滥10/14 136. 只出现一次的数字10/1…...

相似性搜索:第 3 部分--混合倒排文件索引和产品量化
接续前文:相似性搜索:第 2 部分:产品量化 SImilarity 搜索是一个问题,给定一个查询的目标是在所有数据库文档中找到与其最相似的文档。 一、介绍 在数据科学中,相似性搜索经常出现在NLP领域,搜索引擎或推…...

小程序使用uni.createAnimation只执行一次的问题
思路: 在页面创建的时候,创建一个临时动画对象调用 step() 来表示一组动画完成通过动画实例的export方法导出动画数据传递给组件的animation属性还原动画页面卸载的时候,清除动画数据 <template><view class"content"&g…...

win10取消ie浏览器自动跳转edge浏览器
建议大家看完整篇文章再作操作 随着windows10 日渐更新,各种不同的操作,规避IE浏览器跳转Edge浏览器的问题 算了,找了台云机装的server 有自带的IE 1.(失败)思路 协助Edge浏览器 管理员身份打开 PowerShell 一般e…...

目录启示:使用 use 关键字为命名空间内的元素建立非限定名称
文章目录 参考环境三种名称非限定名称限定名称完全限定名称举个栗子 useuse 关键字use ... as .. 命名冲突真假美猴王两个世界 参考 项目描述搜索引擎Bing、GoogleAI 大模型文心一言、通义千问、讯飞星火认知大模型、ChatGPTPHP 官方PHP ManualPHP 官方language.namespaces.ra…...

Go语言介绍与安装
介绍与安装 本教程介绍了 Go,并讨论了选择 Go 相对于其他编程语言的优势。我们还将学习如何在Windows 中安装 Go。 介绍 Go也称为Golang,是由 Google 开发的一种开源、编译型、静态类型的编程语言。 Go创造背后的关键人物是Rob Pike、 Ken Thompson和…...
常用傅里叶变换表
傅里叶展开 傅里叶变换 傅里叶逆变换 时域信号 弧频域信号 线性变换 时域平移 频域平移 伸缩变换 微分性质 逆变换的微分性质 卷积定理 原函数变换结果 单位阶跃函数: 符号函数: 矩形函数: 辛格函数:...
生活中的视音频技术
生活中的视音频技术 平时我们打开电脑中自己存电影的目录的话,一般都会如下图所示,一大堆五花八门的电影。(其实专业的影视爱好者一概会把影视文件分门别类的,但我比较懒,一股脑把电影放在了一起) 因为下载…...

一种用于肽图分析的烷化剂,Desthiobiotin-Iodoacetamide
中文名:脱硫生物素-碘乙酰胺 英文名:Desthiobiotin-Iodoacetamide 化学式:C14H25IN4O3 分子量:424.28 外观:固体/粉末 规格:10mg、25mg、50mg等(接受各种规格的定制服务,具体可…...

【(数据结构) —— 顺序表的应用-通讯录的实现】
(数据结构)—— 顺序表的应用-通讯录的实现 一.通讯录的功能介绍1.基于动态顺序表实现通讯录(1). 功能要求(2).重要思考 二. 通讯录的代码实现1.通讯录的底层结构(顺序表)(1)思路展示(2)底层代码实现(顺序表) 2.通讯录上层代码实现(通讯录结构…...

macbook磁盘清理免费教程分享
笔记本电脑在是我们工作和生活中重要组成部分,磁盘清理是常有的事,而macbook作为其中的代表之一,也越来越受到人们的青睐。然而,如何进行macbook磁盘清理,也事许多人都会遇到的问题,特别是被提示“磁盘已满…...

cartographer_ros数据加载与处理
node_main.cc 坐标系的读取通过tf_bufferautonode类是cartographer_ros接收传感器数据,并传输到cartographer里,同时还会发布map,轨迹等node_options数据传给两个地方,一个是map_builder进行slam操作,一个是node做数据…...