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.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/158225.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

焦炭反应性及反应后强度试验方法

声明 本文是学习GB-T 4000-2017 焦炭反应性及反应后强度试验方法. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 7— 进气口&#xff1b; 8— 测温热电偶。 图 A.1 单点测温加热炉体结构示意图 A.3 温度控制装置 控制精度&#xff1a;(11003)℃。…

JS数组方法map 和 forEach 的区别

一、定义&#xff1a; forEach(): 针对每一个元素执行提供的函数。 map(): 创建一个新的数组&#xff0c;其中每一个元素由调用数组中的每一个元素执行提供的函数得来。 二、区别 1、map 方法返回一个新的数组&#xff0c;而 forEach 方法不会返回任何值&#xff0c;仅仅是遍…

ChatGPT,AIGC 数据库应用 Mysql 常见优化30例

使用ChatGPT,AIGC总结出Mysql的常见优化30例。 1. 建立合适的索引:在Mysql中,索引是重要的优化手段,可以提高查询效率。确保表的索引充分利用,可以减少查询所需的时间。如:create index idx_name on table_name(column_name); 2. 避免使用select * :尽可能指定要返回的…

恒温区检测热电偶

声明 本文是学习GB-T 4000-2017 焦炭反应性及反应后强度试验方法. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 7— 进气口&#xff1b; 8— 测温热电偶。 图 A.1 单点测温加热炉体结构示意图 A.3 温度控制装置 控制精度&#xff1a;(11003)℃。…

文件上传 [MRCTF2020]你传你呢1

题目来源&#xff1a;buuctf [MRCTF2020]你传你&#x1f40e;呢1 打开题目 我们随便上传个木马文件上去 我们bp抓包看看

Linux 系统中提供CPU性能分析工具整理

Linux 系统中提供CPU性能分析工具整理 汇总 查看CPU信息 在linux操作系统中&#xff0c;CPU的信息在启动的过程中被装载到虚拟目录/proc下的cpuinfo文件中&#xff0c;我们可以通过 cat /proc/cpuinfo 查看一下&#xff1a; cat /proc/cpuinfo显示如下&#xff1a; rootthe…

gitlab docker部署,备份,恢复。附踩坑记录

本次安装在CentOS7下进行 1、安装yum 检查是否已经安装yum yum --version如果未安装 sudo yum install -y yum-utils添加镜像源&#xff1a; 国外镜像源&#xff1a;yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo阿里镜像源&am…

Xshell7和Xftp7超详细下载教程(包括安装及连接服务器附安装包)

1.下载 1.官网地址&#xff1a; XSHELL - NetSarang Website 选择学校免费版下载 2.将XSHELL和XFTP全都下载下来 2.安装 安装过程就是选择默认选项&#xff0c;然后无脑下一步 3.连接服务器 1.打开Xshell7&#xff0c;然后新建会话 2.填写相关信息 出现Connection establi…

香港服务器选纯国际线路上网稳定吗?

​  关于香港服务器的线路&#xff0c;我们平时接触较多的分三大类&#xff0c;即纯国际线路、回国专线和香港本地线路。三者价格上存有差距&#xff0c;原因体现在线路和网络质量上&#xff0c;当然这些会关系到服务器的速度和稳定性。譬如&#xff0c;有些用户在选择了纯国…

说明书MS2721A频谱分析仪7.1GHz

安立Anritsu MS2721A 频谱分析仪 MS2721A 是 Anritsu 的 7.1 GHz 频谱分析仪。频谱分析仪测量已知和未知信号的频谱功率。频谱分析仪收集信息&#xff0c;例如输入信号与其频率相比的幅度。作为频率分析仪&#xff0c;频谱分析仪的主要用途是记录和分析电输入信号以及其他信号的…

[23] IPDreamer: Appearance-Controllable 3D Object Generation with Image Prompts

pdf Text-to-3D任务中&#xff0c;对3D模型外观的控制不强&#xff0c;本文提出IPDreamer来解决该问题。在NeRF Training阶段&#xff0c;IPDreamer根据文本用ControlNet生成参考图&#xff0c;并将参考图作为Zero 1-to-3的控制条件&#xff0c;用基于Zero 1-to-3的SDS损失生成…

【想法】取代NI的 PCIe-8371

PCIe-8371 涨价非常厉害。 PCA3 https://www.terasic.com.tw/cgi-bin/page/archive.pl?LanguageEnglish&CategoryNo65&No1143 PCA3 (PCIe Cable Adapter, Gen 3) is a conversion card to connect boards with your host PC. It can support up to PCIe Gen 3 x4. …

文字与视频结合效果

效果展示 CSS 知识点 mix-blend-mode 属性的运用 实现整体页面布局 <section class"sec"><video autoplay muted loop><source src"./video.mp4" type"video/mp4" /></video><h2>Run</h2><!-- 用于切…

ChatGPT或将引发现代知识体系转变

作为当下大语言模型的典型代表&#xff0c;ChatGPT对人类学习方式和教育发展所产生的变革效应已然引起了广泛关注。技术的快速发展在某种程度上正在“倒逼”教育领域开启更深层次的变革。在此背景下&#xff0c;教育从业者势必要学会准确识变、科学应变、主动求变、以变应变&am…

小谈设计模式(28)—解释器模式

小谈设计模式&#xff08;28&#xff09;—解释器模式 专栏介绍专栏地址专栏介绍 解释器模式角色分析抽象表达式&#xff08;Abstract Expression&#xff09;终结符表达式&#xff08;Terminal Expression&#xff09;非终结符表达式&#xff08;Non-terminal Expression&…

【爬虫实战】用pyhon爬百度故事会专栏

一.爬虫需求 获取对应所有专栏数据&#xff1b;自动实现分页&#xff1b;多线程爬取&#xff1b;批量多账号爬取&#xff1b;保存到mysql、csv&#xff08;本案例以mysql为例&#xff09;&#xff1b;保存数据时已存在就更新&#xff0c;无数据就添加&#xff1b; 二.最终效果…

visual studio设置主题和背景颜色

visual studio2019默认的主题有4种&#xff0c;分别是浅白色、深黑色、蓝色、蓝(额外对比度)&#xff0c;背景颜色默认是纯白色RGB(255,255,255)。字体纯白色看久了&#xff0c;眼睛会感到酸痛、疲劳&#xff0c;建议改成浅白RGB(250,250,250)、豆沙绿RGB(85,123,105)、透明蓝白…

语言模型编码中/英文句子格式详解

文章目录 前言一、Bert的vocab.txt内容查看二、BERT模型转换方法(vocab.txt)三、vocab内容与模型转换对比四、中文编码总结 前言 最近一直在学习多模态大模型相关内容&#xff0c;特别是图像CV与语言LLM模型融合方法&#xff0c;如llama-1.5、blip、meta-transformer、glm等大…

Apache Ant的安装

介绍 Apache Ant是一个Java库和一个 命令行工具&#xff0c;可以用来构建Java应用。Ant提供了许多内置的任务&#xff08;tasks&#xff09;&#xff0c;可以编译、组装、测试、运行Java应用。Ant也可以构建非Java应用&#xff0c;例如C、C应用。 Ant非常灵活&#xff0c;没有…

VScode远程root权限调试

尝试诸多办法无法解决的情况下&#xff0c;允许远程登陆用户直接以root身份登录 编辑sshd_config文件 sudo vim /etc/ssh/sshd_config 激活配置 注释掉PermitRootLogin without-password&#xff0c;即#PermitRootLogin without-password 增加一行&#xff1a;PermitRootLo…