C++智能指针之weak_ptr(保姆级教学)

news2024/11/25 22:46:15

目录

C++智能指针之weak_ptr

概述

作用

本文涉及的所有程序

使用说明

weak_ptr的常规操作

lock();

use_count();

expired();

reset();

shared_ptr & weak_ptr

尺寸

智能指针结构框架

常见使用问题

shared_ptr多次引用同一数据,会导致两次释放同一内存(只涉及shared_ptr)

shared_ptr循环引用导致内存泄露(涉及shared_ptr和weak_ptr)

shared_ptr指向局部变量的地址,会导致两次释放同一个内存(只涉及shared_ptr)

shared_ptr接收shared_ptr所实例化对象的this指针导致,会导致两次释放同一个内存(只涉及shared_ptr)


C++智能指针之weak_ptr

概述

std::weak_ptr 是一种智能指针,通常不单独使用,只能和 shared_ptr 类型指针搭配使用,可以视为 shared_ptr 指针的一种辅助工具。借助 weak_ptr 类型指针可以获取 shared_ptr 指针的一些状态信息,比如有多少指向相同空间的 shared_ptr 指针、通过expired()判断shared_ptr 指针指向的堆内存是否已经被释放等等,还可以解决shared_ptr 循环引用的问题。

  • weak_ptr:类模板,弱指针(弱引用计数)
  • weak_ptr弱指针,不会控制影响对象的生命周期(不会改变对象的引用计数),shared_ptr释放指向对象时,是不会考虑weak_ptr是否指向该对象
  • weak_ptr不是独立指针,不能单独操作所指向的资源(不配拥有对象),更不能指向一个新的空间;

作用

  • weak_ptr指针一般用来辅助shared_ptr的使用(监视shared_ptr指向对象的生命周期)
  • weak_ptr和shared_ptr之间可以相互转换,shared_ptr可以直接赋值给weak_ptr,但是反过来是行不通的,需要使用lock函数。

本文涉及的所有程序

00_code.cpp

#include <iostream>
#include <memory>

using namespace std;

class A
{
public:
    A()
    {
        cout << "A" << endl;
    }

    A(int num) : m_num(num)
    {
        cout << "A int" << endl;
    }

    A(const A&& other) : m_num(other.m_num)
    {
        cout << "A move int" << endl;
    }

    ~A()
    {
        cout << "~A" << endl;
    }

public:
    int m_num;
};

int main(int argc, char const* argv[])
{
    shared_ptr<A> pa(new A(5));
    weak_ptr<A> wpa = pa;
    weak_ptr<A> wpa2 = pa;
    weak_ptr<A> wpa3 = pa;

    // weak_ptr常用功能
    auto pb = wpa.lock();
    if (pb == nullptr)
    {
        cout << "pb is nullptr" << endl;
    }

    // use_count();返回的是shared_ptr的引用计数
    cout << wpa.use_count() << endl;

    // expired():判断当前弱指针指向的对象是否被释放
    if (wpa.expired())
    {
        cout << "wpa pointer class is free" << endl;
    }

    wpa.reset();
    return 0;
}

01_code.cpp

#include <iostream>
#include <memory>

using namespace std;

class Child;

class Parent
{
public:
    Parent()
    {
        cout << "Parent" << endl;
    }
    ~Parent()
    {
        cout << "~Parent" << endl;
    }
    //shared_ptr<Child> c;
    weak_ptr<Child> c;
};

class Child
{
public:
    Child()
    {
        cout << "Child" << endl;
    }
    ~Child()
    {
        cout << "~Child" << endl;
    }
    
    shared_ptr<Parent> p;
};

int main(int argc, char const* argv[])
{
    shared_ptr<Parent>pp(new Parent());//pp:1
    shared_ptr<Child>cc(new Child());//cc:1

    //循环引用
    pp->c = cc;//cc:1 pp:1
    cc->p = pp;//pp:2 cc:1

    cout << pp.use_count()<<endl;
    cout << cc.use_count() << endl;

    return 0;
}

02_code.cpp

#include <iostream>
#include <memory>

using namespace std;

int main(int argc, char const *argv[])
{
    shared_ptr<int> p(new int(5));
    weak_ptr<int> wp = p;

    //shared_ptr/weak_ptr的尺寸大小是裸指针的两倍
    cout << sizeof(int *) << endl;
    cout << sizeof(p) << endl;
    cout << sizeof(wp) << endl;

    return 0;
}

03_code.cpp

#include <iostream>
#include <memory>

using namespace std;

class A:public enable_shared_from_this<A>
{
public:
    A()
    {
        cout << "A" << endl;
    }

    A(int num) : m_num(num)
    {
        cout << "A int" << endl;
    }

    A(const A &&other) : m_num(other.m_num)
    {
        cout << "A move int" << endl;
    }
    shared_ptr<A> getAddr()
    {
        //return shared_ptr<A>(this);
        return shared_from_this();//返回可共享的this指针
    }

    ~A()
    {
        cout << "~A" << endl;
    }

public:
    int m_num;
};

int main(int argc, char const *argv[])
{
    // A a;
    // shared_ptr<A>temp(&a);
    // shared_ptr<A> temp = a.getAddr();

    // int num = 5;
    // shared_ptr<int>p(&num);

    //shared_ptr<A> pa(new A());
    A *pa = new A();
    shared_ptr<A> temp = pa->getAddr();
    return 0;
}

使用说明

在VS2022中进行调试,执行完第一条语句后,pa的强引用计数加1

执行完第二句的弱指针赋值后,发现多了一个弱引用计数,和强引用计数一样都为1

增加pa.reset()的操作。通过调试可以发现:不关心是否有弱指针指向当前对象,只要指向当前的指针强引用计数为0了,当前对象就会调用析构函数释放空间。

weak_ptr无法指向一个新的空间(只能指向已有的智能指针),它不配拥有一个对象,只能作为一个指向

weak_ptr不可以直接赋值给shared_ptr

weak_ptr的常规操作

lock();

获取弱指针指向的对象对应的共享指针,如果指向的对象释放,那么返回一个nullptr

调用lock函数来获得shared_ptr(如果对象已经被释放,则返回一个空的shared_ptr)

(有些书上叫做将弱指针转换为共享指针)

在VS2022下调试结果如下:

在调用lock前,pa的强引用计数为1

在调用lock后,pa的强引用计数变为2

use_count();

功能:返回有多少个shared_ptr智能指针指向某对象;(引用计数的个数)

用途:主要用于调试

expired();

判断弱指针是否过期(所指向的对象是否被释放true/false)

reset();

将该弱指针设置为空,弱引用计数减1,强引用计数不变

执行wpa.reset前,弱引用计数为3,强引用计数为2

执行wpa.reset后,弱引用计数减1,变为2;强引用计数仍为2

shared_ptr & weak_ptr

尺寸

shared_ptr和weak_ptr一样大,是裸指针的两倍;

智能指针结构框架

从中可以发现智能指针实际上由两个指针组成:一个指针指向数据,一个指针指向控制块

常见使用问题

shared_ptr多次引用同一数据,会导致两次释放同一内存(只涉及shared_ptr)

int* pInt = new int[100];

shared_ptr sp1(pInt);

// 一些其它代码之后…

shared_ptr sp2(pInt);

shared_ptr循环引用导致内存泄露(涉及shared_ptr和weak_ptr)

我们定义了两个类:Parent和Child,两个类没有继承关系;在Parent中定义了一个Child的智能指针,在Child中定义了一个指向Parent类型的智能指针

在main函数中,定义分别定义Parent和Child类型的指针,让它们内部的指针互相指向

这样就产生了循环引用的现象

编译报错,这是由于未前置声明Child类,Parent类中找不到Child

加上前置声明

重新编译运行结果如下:发现两个类只构造了,没有析构释放,导致了内存泄漏

通过VS2022调试可以发现,两个main中的智能指针在循环引用后,引用计数都变成了2。在程序运行结束时,main中的两个智能指针释放了之后,引用计数减1后变为1,大于0;而两个在类中定义的智能指针,由于它们属于类中的属性,它们必须在析构函数被调用了才能释放,而程序结束引用计数不为0,也就无法调用析构函数。因此这样就导致了内存泄漏。

以图示说明如下:

解决方法:我们将类中的两个指针随便一个改为weak_ptr

如图,我修改的是Parent中的指针,运行发现两个对象空间可以被正常释放

分析:由于Parent类中的是weak_ptr,因此执行完p->c = cc;cc->p = pp;后,cc的强引用计数不变,仍为1,pp的强引用计数为2;当main中的return 0;执行完之后,局部变量释放,pp引用计数变成1,cc引用计数变为0,从而会调用Child的析构函数,将Child类中的shared_ptrp释放,因此pp的引用计数也变为0,最终调用Parent的析构函数,将全部空间释放掉。

shared_ptr指向局部变量的地址,会导致两次释放同一个内存(只涉及shared_ptr)

我们在类中定义了一个函数,用于返回当前对象的地址,其中this指针使用shared_ptr进行包装。

在main中实例化一个对象,并用一个智能指针来获取对象地址。

发现报错:段错误,局部对象被释放了两次

这是由于a是局部对象,它在程序运行结束的时候会自己调用析构函数进行释放,而temp是指向这个局部变量的智能指针,它在程序结束的时候会再次释放局部变量,因此导致了空间被释放两次,产生了段错误。与下图情况一模一样

同样使用智能指针接收对象的this指针也不行

解决方法:

通过裸指针申请空间的方法,实例化对象,然后再用智能指针接收对象返回值

shared_ptr接收shared_ptr所实例化对象的this指针导致,会导致两次释放同一个内存(只涉及shared_ptr)

继续以上面的class A为例,通过智能指针实例化从堆区new出来的对象,通过智能指针接收对象的this指针,也会导致空间被释放两次

解决方法:

针对通过智能指针实例化从堆区new出来的对象,通过智能指针接收对象的地址。而对于任何局部变量此方法无效(我们也可以使用上面的方法,直接使用裸指针从堆区实例化对象)

我们需要继承一个模板类enable_shared_from_this,并将要返回的this指针改为shared_from_this(),此方法可以返回可共享的this指针

运行结果:

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Medium:How to check the correctness of the AB test?

有以下两种错误&#xff1a; 通常&#xff0c;type 1 error is more important&#xff01;因此我们type 2 error就是在“委曲求全”&#xff1a; The probability of Type II error can be adjusted to the desired value by changing the size of the groups or by reducing…

【LeetCode每日一题合集】2023.8.14-2023.8.20(⭐切披萨3n块披萨)

文章目录 617. 合并二叉树833. 字符串中的查找与替换&#xff08;模拟&#xff09;2682. 找出转圈游戏输家&#xff08;模拟&#xff09;1444. 切披萨的方案数&#xff08;⭐⭐⭐⭐⭐&#xff09;解法——从递归到递推到优化&#xff08;二维前缀和记忆化搜索&#xff09; 1388…

【LeetCode每日一题合集】2023.8.7-2023.8.13(动态规划分治)

文章目录 344. 反转字符串1749. 任意子数组和的绝对值的最大值&#xff08;最大子数组和&#xff09;1281. 整数的各位积和之差1289. 下降路径最小和 II解法1——动态规划 O ( n 3 ) O(n^3) O(n3)解法2——转移过程优化 O ( n 2 ) O(n^2) O(n2) ⭐ 1572. 矩阵对角线元素的和解法…

13.Redis 事务

Redis 事务 redis 事务事务操作multi 开启事务exec 执行事务discard 放弃当前事务watchunwatch redis 事务 Redis 的事务和 MySQL 的事务概念上是类似的。 都是把⼀系列操作绑定成⼀组。 让这⼀组能够批量执⾏。 Redis 的事务和 MySQL 事务的区别: 弱化的原⼦性: 这里指的是 …

Pinely Round 2 (Div. 1 + Div. 2) G. Swaps(组合计数)

题目 给定一个长度为n(n<1e6)的序列&#xff0c;第i个数ai(1<ai<n)&#xff0c; 操作&#xff1a;你可以将当前i位置的数和a[i]位置的数交换 交换可以操作任意次&#xff0c;求所有本质不同的数组的数量&#xff0c;答案对1e97取模 思路来源 力扣群 潼神 心得 感…

huggingface下载模型文件(基础入门版)

huggingface是一个网站&#xff0c;类似于github&#xff0c;上面拥有众多开源的模型、数据集等资料&#xff0c;人工智能爱好者可以很方便的上面获取需要的数据&#xff0c;也可以上传训练好的模型&#xff0c;制作的数据集等。本文只介绍下载模型的方法&#xff0c;适合新手入…

SpringBoot连接MySQL数据库,使用Mybatis框架(入门)

1. 说明 SpringBoot项目&#xff0c;连接MySQL数据库&#xff0c;使用Mybatis框架。 本篇文章作为 SpringBoot 使用 Mybatis 的入门。 2. 依赖 2.1. MySQL驱动依赖 MySQL驱动&#xff0c;使用SpringBoot版本对应的默认版本&#xff0c;不需要手动指定版本。 比如&#xf…

【狂神】Spring5 (三) 之Aop的实现方式

今天没有偷懒&#xff0c;只是忘了Mybatis&#xff0c;所以去补课了~ ┏━━━━━━━━━━━━━━━┓ NICE PIGGY PIG.. ┗━━━━━━━△━━━━━━━┛ ヽ(&#xff65;ω&#xff65;)&#xff89; | / UU 1.Aop实现方式一 1.1、什…

UG\NX CAM二次开发 查询工序所在的方法组TAG UF_OPER_ask_method_group

文章作者:代工 来源网站:NX CAM二次开发专栏 简介: UG\NX CAM二次开发 查询工序所在的方法组TAG UF_OPER_ask_method_group 效果: 代码: void MyClass::do_it() { int count=0;tag_t * objects;UF_UI_ONT_ask_selected_nodes(&count, &objects);for (i…

Multimedia-播放器-架构2

目录 引言 问题1&#xff1a; 数据缓冲区 多线程模型 缓冲区的特点&#xff1a; 点播和直播场景中的缓冲区&#xff1a; 问题2&#xff1a; 同步方式 同步实现过程 引言 上一篇梳理了播放器的基本工作与处理流程&#xff0c;本片内容主要梳理一下其中会遇到的问题&am…

桂理理工大题

#include <stdio.h> #include <stdlib.h>int getMax(int n); int getMin(int n); int range(int n); static int count1; //作为全局变量控制每次的序列号int main(){int num;int i,j;do{printf("输入黑洞数&#xff1a;\n");scanf("%d",&…

Jdk8 动态编译 Java 源码为 Class 文件(三)

Jdk8 动态编译 Java 源码为 Class 文件 一.JDK版本二.工程介绍1.依赖2.启动类3.配置类&#xff08;用于测试依赖注入&#xff09;4.工具类1.Java 源码文件读取类2.SpringBoot 容器实例管理类 5.测试类1.抽象类2.接口类3.默认抽象实现4.默认接口实现 6.接口类1.测试接口2.类重载…

数学建模:模糊综合评价分析

&#x1f506; 文章首发于我的个人博客&#xff1a;欢迎大佬们来逛逛 数学建模&#xff1a;模糊综合评价分析 文章目录 数学建模&#xff1a;模糊综合评价分析综合评价分析常用评价方法一级模糊综合评价综合代码 多级模糊综合评价总结 综合评价分析 构成综合评价类问题的五个…

Emmet 使用笔记小结

Emmet 使用笔记小结 最近在跟视频走 CSS 的教程&#xff0c;然后要写很多的 HTML 结构&#xff0c;就想着总结一下 Emmet 的语法。 Emmet 是一个工具可以用来加速 HTML 和 CSS 的开发过程&#xff0c;不过 emmet 只支持 HTML & XML 文件结构&#xff0c;所以我个人觉得对…

【JavaSE】面试01

文章目录 1. JDK、JRE、JVM之间的关系2. 补充3. 面试题&#xff1a;重载和重写的区别&#xff1f;4. super和this5. &#xff08;重点&#xff01;&#xff01;&#xff09;若父类和子类均有静态代码块、实例代码块以及无参构造方法&#xff0c;则继承关系上的执行顺序&#xf…

如何解决分库分表主键问题?

分析&回答 从问题角度出发&#xff1a;我们需要一个全局唯一的 id 来支持&#xff0c;排序问题等。这都是你实际生产环境中必须考虑的问题。可以先看下我们之前的文章分布式系统唯一ID如何生成&#xff1f; 雪花算法和雪花算法的变种是大家常用的 喵呜面试助手&#xff1…

Python字节码文件

迷途小书童的 Note 读完需要 5分钟 速读仅需 2 分钟 大家好&#xff0c;我是迷途小书童&#xff01; 今天给大家介绍一个神奇的文件 -- pyc 文件&#xff0c;它能加速 Python 程序的执行速度&#xff0c;同时也能起到保护源码的作用。 1 什么是 pyc 文件? pyc 文件是经过编译的…

PYTHON-“人生重开模拟器“

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是Aileen★。希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f194;本文由 Aileen_0v0★ 原创 CSDN首发&#x1f412; 如需转载还…

SAP-PP:基础概念笔记-5(物料主数据的MRP1~4视图)

文章目录 前言一、MRP1视图Base Unit of Measure&#xff08;UoM&#xff09;MRP 组采购组ABC 指示器Plant-Specific Material Status 特定的工厂物料状态MRP 类型 MRP TypeMRP 类型 MRP TypeMaster Production Scheduling(MPS) 主生产计划基于消耗的计划(CBP)再订货点Reorder-…

【业务功能篇91】微服务-springcloud-多线程-线程池执行顺序

一、线程的实现方式 1. 线程的实现方式 1.1 继承Thread class ThreadDemo01 extends Thread{Overridepublic void run() {System.out.println("当前线程:" Thread.currentThread().getName());} }1.2 实现Runnable接口 class ThreadDemo02 implements Runnable{…