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

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 种方法:

  1. 调用 expired() 方法,如果 weak_ptr 不再共享一个对象则返回 true。这与检查 use_count() 是否等于 0 是等价的,但可能运行速度更快
  2. 可以显式将 weak_ptr 使用对应构造函数转换为 shared_ptr。如果此时没有合法的引用对象,则这个构造函数抛出一个 bad_weak_ptr 异常。 这是一个派生自 std::exception 的一个异常,what() 返回 bad_weak_ptr (每个设备上实现有所差异)。
  3. 可以调用 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 的引入要解决普通指针存在的一些问题一样&#xff0c;weak_ptr 的引入&#xff0c;也是因为 shared_ptr 本身在某些情况下&…...

540 - Team Queue (UVA)

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

投资组合之如何估值

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

2024届通信工程保研经验分享(预推免入营即offer)

2024届通信工程保研经验分享&#xff08;预推免入营即offer&#xff09; BackGround夏令营情况&#xff1a;预推免情况&#xff1a; BackGround 本科院校&#xff1a;末九 专业&#xff1a;通信工程 rank&#xff1a;3/123&#xff08;预推免绩点排名&#xff09;&#xff0…...

L2-025 分而治之 - java

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

Python+高光谱数据预处理-机器学习-深度学习-图像分类-参数回归

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

免费 AI 编程助手 Amazon CodeWhisperer 体验

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

【Linux】从零开始学习Linux基本指令(一)

&#x1f6a9;纸上得来终觉浅&#xff0c; 绝知此事要躬行。 &#x1f31f;主页&#xff1a;June-Frost &#x1f680;专栏&#xff1a;Linux入门 &#x1f525;该文章主要了解Linux操作系统下的基本指令。 目录&#xff1a; ⌛️指令的理解⏳目录和文件的理解⏳一些常见指令✉…...

Java GC 算法

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

vue3 v-html中使用v-viewer

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

Leetcode算法解析——查找总价格为目标值的两个商品

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

unity游戏开发引擎unity3D开发

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

iptables

目录 iptables 匹配规则&#xff1a;由上到下依次匹配&#xff0c;一旦匹配不再匹配 参数 知识点 REJECT与DROP REJECT与DROP的区别 当使用的时REJECT时&#xff0c;客户端访问迅速返回的值是拒绝连接 当使用的是DROP时&#xff0c;返回的时连接超时 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 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 …...

Spark入门

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

react–antd 实现TreeSelect树形选择组件,实现点开一层调一次接口

效果图: 注意: 当选择“否”&#xff0c;开始调接口&#xff0c;不要把点击调接口写在TreeSelect组件上&#xff0c;这样会导致问题出现&#xff0c;没有层级了 部分代码:...

android 固定进度环形刷新效果

android 固定进度无限旋转的环形效果 效果图 效果视频&#xff1a; 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

参考&#xff1a;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

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

相似性搜索:第 3 部分--混合倒排文件索引和产品量化

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

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

label-studio的使用教程(导入本地路径)

文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

2.Vue编写一个app

1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...

ServerTrust 并非唯一

NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...

python执行测试用例,allure报乱码且未成功生成报告

allure执行测试用例时显示乱码&#xff1a;‘allure’ &#xfffd;&#xfffd;&#xfffd;&#xfffd;&#xfffd;ڲ&#xfffd;&#xfffd;&#xfffd;&#xfffd;ⲿ&#xfffd;&#xfffd;&#xfffd;Ҳ&#xfffd;&#xfffd;&#xfffd;ǿ&#xfffd;&am…...

深度学习习题2

1.如果增加神经网络的宽度&#xff0c;精确度会增加到一个特定阈值后&#xff0c;便开始降低。造成这一现象的可能原因是什么&#xff1f; A、即使增加卷积核的数量&#xff0c;只有少部分的核会被用作预测 B、当卷积核数量增加时&#xff0c;神经网络的预测能力会降低 C、当卷…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的

修改bug思路&#xff1a; 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑&#xff1a;async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...

现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?

现有的 Redis 分布式锁库&#xff08;如 Redisson&#xff09;相比于开发者自己基于 Redis 命令&#xff08;如 SETNX, EXPIRE, DEL&#xff09;手动实现分布式锁&#xff0c;提供了巨大的便利性和健壮性。主要体现在以下几个方面&#xff1a; 原子性保证 (Atomicity)&#xff…...

uniapp手机号一键登录保姆级教程(包含前端和后端)

目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号&#xff08;第三种&#xff09;后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...