weak_ptr 与 一个难发现的错误(循环依赖问题)笔记

推荐B站视频:7.weak_ptr与一个非常难发现的错误_哔哩哔哩_bilibiliicon-default.png?t=N7T8https://www.bilibili.com/video/BV18B4y187uL/?p=7&spm_id_from=pageDriver&vd_source=a934d7fc6f47698a29dac90a922ba5a3一、weak_ptr    

  • weak_ptr并不拥有所有权
  • 并不能调用 -> 和 解引用*

准备好头文件和源文件:

  • cat.h
#ifndef CAT_H
#define CAT_H
#include <string>
#include <iostream>
class Cat{
public:
    Cat(std::string name);
    Cat() = default;
    ~Cat();
    void catInfo() const {
        std::cout<<"cat info name : "<<m_name<<std::endl;
    }
    std::string get_name() const{
        return m_name;
    }
    void set_cat_name(const std::string &name) {
        this->m_name = name;
    }
private:
    std::string m_name{"Mimi"};
};
#endif
  • cat.cpp
#include "cat.h"
Cat::Cat(std::string name) :m_name(name) {
    std::cout<<"Constructor of Cat : "<<m_name<<std::endl;
}
 
Cat::~Cat() {
    std::cout<<"Destructor of Cat"<<std::endl;
}
  • main.cpp
#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;
int main(int argc,char* argv[]) {
    std::shared_ptr<Cat> s_p_c1 = std::make_shared<Cat>("c1");
    std::weak_ptr<Cat> w_p_c1(s_p_c1);
    // use_count()
    cout<<"w_p_c1: " << w_p_c1.use_count() << endl; // 1
    cout<<"s_p_c1: " << s_p_c1.use_count() << endl; // 1
    // w_p_c1->catInfo();// error C2039: "catInfo": 不是 "std::weak_ptr<Cat>" 的成员

    std::shared_ptr<Cat> s_p_c2 = w_p_c1.lock();
    cout<<"w_p_c1: " << w_p_c1.use_count() << endl; // 2
    cout<<"s_p_c1: " << s_p_c1.use_count() << endl; // 2
    cout<<"s_p_c2: " << s_p_c2.use_count() << endl; // 2

    cout<<"over~"<<endl; 
    return 0;
}

二、一个难发现的错误(循环依赖问题

(1)weak_ptr   为什么会存在呢?

  • A类中有一个需求需要存储其他A类对象的信息
  • 如果使用shared_ptr,那么在销毁时会遇到循环依赖问题(Cyclic dependency problem)
  • 所以我们这里需要用一个不需要拥有所有权的指针来标记该同类对象
    • weak_ptr可以通过lock()函数来提升为shared_ptr(类型转换)

比方说我是一个Person,需要存储朋友的信息,需要用一个指针来指向另外一个人类,如果使用shared_ptr,那么在销毁时会遇到循环依赖问题(Cyclic dependency problem)。我在销毁的时候,我需要销毁我的朋友,我的朋友也需要销毁我,这样就出现了循环依赖问题。不知道谁先销毁,谁后销毁。所以我们这里需要用一个不需要拥有所有权的指针来标记该同类对象。

修改头文件cat.h

#ifndef CAT_H
#define CAT_H
#include <string>
#include <iostream>
#include <memory>
class Cat{
public:
    Cat(std::string name);
    Cat() = default;
    ~Cat();
    void catInfo() const {
        std::cout<<"cat info name : "<<m_name<<std::endl;
    }
    std::string get_name() const{
        return m_name;
    }
    void set_cat_name(const std::string &name) {
        this->m_name = name;
    }
    void set_friend(std::shared_ptr<Cat> cat) {   // 增加该函数
        m_friend = cat;
    }
private:
    std::string m_name{"Mimi"};
    std::shared_ptr<Cat> m_friend; // 增加该变量
};
#endif

main.cpp

#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;
int main(int argc,char* argv[]) {
    std::shared_ptr<Cat> c3 = std::make_shared<Cat>("C3");
    std::shared_ptr<Cat> c4 = std::make_shared<Cat>("C4");
    cout<<"over~"<<endl; 
    return 0;
}

 执行结果,可以正常调用析构函数:

PS D:\Work\C++UserLesson\cppenv\bin\Debug> ."D:/Work/C++UserLesson/cppenv/bin/Debug/app.exe"
Constructor of Cat : C3
Constructor of Cat : C4
over~
Destructor of Cat
Destructor of Cat
PS D:\Work\C++UserLesson\cppenv\bin\Debug>

 可是如果让C3和C4互为朋友

#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;
int main(int argc,char* argv[]) {
    std::shared_ptr<Cat> c3 = std::make_shared<Cat>("C3");
    std::shared_ptr<Cat> c4 = std::make_shared<Cat>("C4");

    c3->set_friend(c4);
    c4->set_friend(c3);
    cout<<"over~"<<endl; 
    return 0;
}

执行结果,发现无法正常调用析构函数:

PS D:\Work\C++UserLesson\cppenv\bin\Debug> ."D:/Work/C++UserLesson/cppenv/bin/Debug/app.exe"
Constructor of Cat : C3
Constructor of Cat : C4
over~
PS D:\Work\C++UserLesson\cppenv\bin\Debug>

 (2)解决方案:将头文件的m_friendshared_ptr修改成weak_ptr即可

std::shared_ptr<Cat> m_friend; 
        ||
        ||  (修改成这样)
        ||
std::weak_ptr<Cat> m_friend; 

执行结果,发现可以正常调用析构函数:

PS D:\Work\C++UserLesson\cppenv\bin\Debug> ."D:/Work/C++UserLesson/cppenv/bin/Debug/app.exe"
Constructor of Cat : C3
Constructor of Cat : C4
over~
Destructor of Cat
Destructor of Cat
PS D:\Work\C++UserLesson\cppenv\bin\Debug>

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【MySQL】如何使用图形化界面DataGrip操作数据库

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-W5JDg0WA1tjEP66Y {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…

uniapp Android 离线打包之未配置appkey或配置错误

1、去官网申请appKey: 申请Appkey 2、项目中使用appKey: <meta-dataandroid:name"dcloud_appkey"android:value"794534204bbae06989........" />3、参考 官方教程&#xff0c;修改配置&#xff1a; 配置教程 注意&#xff1a; 本地的appId 和 官…

如何解决Flutter应用程序的兼容性问题

随着移动应用开发领域的不断发展&#xff0c;Flutter作为一种跨平台框架&#xff0c;受到了越来越多开发者的青睐。要确保Flutter应用程序能够在不同的设备和操作系统上稳定运行&#xff0c;并提供一致的用户体验&#xff0c;我们需要重视应用程序的兼容性问题。下面将简单的介…

【代码随想录-数组】有序数组的平方

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老导航 檀越剑指大厂系列:全面总结 jav…

HTML-表单

表单 概念&#xff1a;一个包含交互的区域&#xff0c;用于收集用户提供的数据。 1.基本结构 示例代码&#xff1a; <form action"https://www.baidu.com/s" target"_blank" method"get"><input type"text" name"wd&q…

独立站怎么建设对seo好?

现如今市面上就有不少开源的建站程序可供挑选&#xff0c;哪怕你不懂技术&#xff0c;不懂代码&#xff0c;也能建自己的独立站&#xff0c;效果比不少所谓的用自己技术开发的站都要好&#xff0c;本身做一个网站不难&#xff0c;但你做网站的目的是什么&#xff1f;是为了在搜…

ctf-idea调试jar包

0.拿到jar包并解压 进入解压出来的目录,然后以该目录打开项目 1.设置maven 设不设置都行 2.添加依赖 添加两个依赖, boot-inf下的 classes和lib 3.配置调试器 添加 remote jvm debug 1.根据jdk版本选择调试参数 2.选择module classpath为解压后的文件夹名 如图,运行jar包的…

代码随想录Day31 | 贪心算法 455.分发饼干 376. 摆动序列 53. 最大子序和

代码随想录Day31 | 贪心算法 455.分发饼干 376. 摆动序列 53. 最大子序和 贪心算法455.分发饼干376.摆动序列53.最大子序和 贪心算法 局部最佳 --> 全局最佳 刷题或者面试的时候&#xff0c;手动模拟一下感觉可以局部最优推出整体最优&#xff0c;而且想不到反例&#xff0…

【QT+QGIS跨平台编译】之九:【LZ4+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

文章目录 一、LZ4介绍二、文件下载三、文件分析四、pro文件五、编译实践一、LZ4介绍 LZ4是一种无损压缩算法,压缩速度为每核心400MB/s。 LZ4是目前效率最高的压缩算法,更加侧重于压缩/解压缩速度,压缩比并不突出,本质上就是时间换空间。 LZ4库是使用BSD许可证作为开放源码…

Linux——shell程序的简单实现

shell程序的简单实现 本章思维导图&#xff1a; 注&#xff1a;本章思维导图对应的.xmind和.png文件都已同步导入至资源&#xff0c;可免费查阅 在学习完有关进程的知识后&#xff0c;我们就可以开始尝试自己实现一个简单的shell程序了。 注&#xff1a;在编写简单的shell程…

Linux实现:从倒计时到进度条

文章目录 1.回车与换行2.缓冲区的概念3.倒计时4.进度条(第一版无应用场景)5.进度条(第二版有应用场景) 1.回车与换行 2.缓冲区的概念 强制刷新可以使用冲刷函数fflush #include <stdio.h> #include <unistd.h> int main() {printf("I am a \nhandsome man!&q…

leetcode 第三弹

链表声明&#xff1a; * Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullptr) {}* ListNode(int x, ListNode *next) : val(x), next(n…

【K12】tk窗口+plt图像功能-学习物理中的串并联研究【附源码说明】

程序源码 import tkinter as tk import matplotlib.pyplot as plt# 初始化 matplotlib 的字体设置 plt.rcParams[font.family] SimHei# 计算串联电路的函数 def calculate_series():try:# 获取用户输入的电阻值并转换为浮点数r1 float(entry_r1.get())r2 float(entry_r2.ge…

【CANoe使用大全】——Trace窗口

&#x1f64b;‍♂️【CANoe使用大全】系列&#x1f481;‍♂️点击跳转 文章目录 1.Trace作用2.Trace窗口打开方式2.1.Analysis—>Trace2.2.Measurement Setup ------> Trace 3.Trace窗口菜单栏介绍3.1. Detail View3.1. Statistic View3.3.Difference view3.4.Predefi…

【开发问题问题解决开发小技巧】通用资源管理01

【问题】新增应该输出提示但是出现乱码 查看会话发现是会话已结束&#xff0c;好家伙 重新登录会话依旧新增失败&#xff0c; 原来是提交的项没添加ORZ 【问题】会话保护 将会话保护改为“无限制” 执行修改提交但是一直在加载中&#xff0c;回滚后执行直接跳出来“未找到驱动程…

js打地鼠

文章目录 1实现效果2代码实现 1实现效果 游戏难度&#xff1a;简单&#xff0c;一般&#xff0c;困难&#xff0c;噩梦&#xff08;控制setInterval的time参数&#xff09; 按钮功能&#xff1a;结束&#xff08;可以通过修改gameScore的值来修改判定结束的分数&#xff09;&am…

MySQL十部曲之四:MySQL中的数据类型

文章目录 前言概述数字类型数字类型语法数字类型字面量十六进制字面量位字面量布尔字面量 数字类型的属性超出范围和溢出处理 时间和日期类型时间和日期类型语法DATE、DATETIME和TIMESTAMP的异同TIMESTAMP和DATETIME的自动初始化和更新时间和日期字面量 字符串类型字符串类型语…

知识圣殿,智慧熔炉

知识圣殿&#xff0c;智慧熔炉 知识殿堂&#xff0c;巍然屹立 一座灵魂熔炉&#xff0c;号称图书馆 万卷书香盈架&#xff0c;智慧如星河汇聚 每一册书页&#xff0c;流淌着人类文明的血脉 钢笔与墨水交织诗篇 思想发芽&#xff0c;真理绽放光焰 浩瀚知识海洋&#xff0c;波涛…

tensorboard+seaborn 画RL论文图片

概要 tensorboard记录数据&#xff0c;并保存为fie_name.csv 文件加载file_name.csv文件, 处理加载得到数据,然后通过seaborn 显示出来。 1. tensorboard 通常来说&#xff0c;我们一般会用 tensorboard 去记录一些数据。 所以我们先介绍一下 tensorboard 一些注意事项 seti…

mybatis-plus常用使用方法

** mybaits-plus常用使用方法 ** 常用三层分别继承方法 1.1mapper层&#xff08;接口定义层&#xff09;可以用BaseMapper<> 例如&#xff1a; 1.2.里面常用的封装方法有 1.3常用方法介绍 【添加数据&#xff1a;&#xff08;增&#xff09;】int insert(T entity);…