C++标准库学习之 weak_ptr 智能指针

在上一篇文章中介绍了 shared_ptr 这种智能指针,他为了解决在指针没有引用的情况下自动回收资源这种情况而产生的,但是在部分情况下会产生另一种问题,那就是两个智能指针被循环引用 , a 使用 shared_ptr 引用了b ,同时 b 使用 shared_ptr 引用了 a,在 方法执行完成后 ,a 与 b 的智能指针都会被 释放,但是 由于他们互相持有 , a 与 b 的 used_count >0 ,那么就不会释放他们的资源,看下面的例子

c 复制代码
#include <iostream>
#include <memory>
#include <vector>


using namespace std;


class Person{
public:
    // 构造方法 ,name 是必要参数  mum 与 dad 是非必要参数 
    Person(string name,shared_ptr<Person> mum = nullptr ,shared_ptr<Person> dad = nullptr)
    :name(name) ,mum(mum),dad(dad)// 此种初始化方式被经常应用到 jni 源码上
    {

    }

    // 析构函数,资源被释放时会被调用
    ~Person(){
        cout<<"对象名称是->" << name<< "被回收了"<<endl;
    }

    // 向量 
    vector<shared_ptr<Person>> child;
    
    //名字
    string name;
    
    // 智能指针引用mum
    shared_ptr<Person> mum;
    
    //智能指针引用dad
    shared_ptr<Person> dad;
};

// 初始化 家人,让他们循环引用
shared_ptr<Person> getFamily(const string& name){

    shared_ptr<Person> mum(new Person(name +"'s Mum"));

    shared_ptr<Person> dad(new Person(name +"'s Dad"));

    shared_ptr<Person> child(new Person(name,mum,dad));

    mum->child.push_back(child);

    dad->child.push_back(child);

    return child;
}


int main(){


    shared_ptr<Person> tsm=  getFamily("tsm");

    cout<< "------------before  change-----------"<<endl;

    cout << tsm->dad->name<<endl;
    cout << tsm->mum->name<<endl;
    cout << tsm->name<<endl;

    cout<<"tsm 的引用个数" <<tsm.use_count()<<endl;

    cout<< "------------start change-----------"<<endl;

    tsm = getFamily("tsm1");

    cout<< "------------after change-----------"<<endl;

    system("pause");

    return 0;
}

结果:

lua 复制代码
D:\CWorkSpace\tsmTest\cmake-build-debug\tsmTest.exe
------------before  change-----------
tsm's Dad
tsm's Mum
tsm
tsm 的引用个数3
------------start change-----------
------------after change-----------
. . . pause . . . 

发现在为 tsm 这个变量二次赋值时,第一次 shared_ptr 指针指向的资源内存应该是被释放的,但是他们的析构函数并没有被执行,那就证明他的资源并没有被释放,那么我们如何来修改内,这就引出了我们今天的主角 weak_ptr

修改如下

arduino 复制代码
class Person{
public:
    // 构造方法 ,name 是必要参数  mum 与 dad 是非必要参数
    Person(string name,shared_ptr<Person> mum = nullptr ,shared_ptr<Person> dad = nullptr)
    :name(name) ,mum(mum),dad(dad)// 此种初始化方式被经常应用到 jni 源码上
    {

    }

    // 析构函数,资源被释放时会被调用
    ~Person(){
        cout<<"对象名称是->" << name<< "被回收了"<<endl;
    }

    // 将向量中的引用使用  weak_ptr ,打破互相引用即可
    vector<weak_ptr<Person>> child;

    //名字
    string name;

    // 智能指针引用mum
    shared_ptr<Person> mum;

    //智能指针引用dad
    shared_ptr<Person> dad;
};

修改后的结果如下:

rust 复制代码
------------before  change-----------
tsm's Dad
tsm's Mum
tsm
tsm 的引用个数1
------------start change-----------
对象名称是->tsm被回收了
对象名称是->tsm's Dad被回收了
对象名称是->tsm's Mum被回收了
------------after change-----------

发现在使用 weak_ptr 打破互相引用的后, 被覆盖的 tsm 的析构函数被成功调用了,

weak_ptr 的使用场景主要有2个

1: 打破 shared_ptr 使用混乱出现的情况导致循环引用的情况,

2: 如果持有该 shared_ptr 的对象的生命周期是大于 该 shared_ptr 指针的生命周期的情况,就会导致 shared_ptr 资源无法被回收,

weak_ptr 的创建方式有3中

1:

arduino 复制代码
weak_ptr<Person> t0; 构造空对象

2:

arduino 复制代码
weak_ptr<Person> t1(t0); 使用拷贝构造函数

3: 使用 shared_ptr 对象初始化

ini 复制代码
shared_ptr<Person> tsm=  getFamily("tsm");

weak_ptr<Person> t2(tsm);

检查weak_ptr 是否还有效

scss 复制代码
t2.expired() 

expired 源码如下

arduino 复制代码
bool
expired() const noexcept
{ return _M_refcount._M_get_use_count() == 0; }

可以看到 返回的是 use_count ,那就证明 if(!t2.expired) 则证明他是有效的

使用 weak_ptr,使用lock 方法将 weak_ptr 转换成 shared_ptr ,防止在使用过程中资源被回收

ini 复制代码
shared_ptr<Person> tt =  t2.lock();

看一下lock 的源码

arduino 复制代码
shared_ptr<_Tp>
lock() const noexcept
{ return shared_ptr<_Tp>(*this, std::nothrow); }
相关推荐
tan180°2 小时前
MySQL表的操作(3)
linux·数据库·c++·vscode·后端·mysql
彭祥.4 小时前
Jetson边缘计算主板:Ubuntu 环境配置 CUDA 与 cudNN 推理环境 + OpenCV 与 C++ 进行目标分类
c++·opencv·分类
lzb_kkk4 小时前
【C++】C++四种类型转换操作符详解
开发语言·c++·windows·1024程序员节
胖大和尚6 小时前
clang 编译器怎么查看在编译过程中做了哪些优化
c++·clang
钱彬 (Qian Bin)7 小时前
一文掌握Qt Quick数字图像处理项目开发(基于Qt 6.9 C++和QML,代码开源)
c++·开源·qml·qt quick·qt6.9·数字图像处理项目·美观界面
双叶8368 小时前
(C++)学生管理系统(正式版)(map数组的应用)(string应用)(引用)(文件储存的应用)(C++教学)(C++项目)
c语言·开发语言·数据结构·c++
源代码•宸8 小时前
C++高频知识点(二)
开发语言·c++·经验分享
jyan_敬言9 小时前
【C++】string类(二)相关接口介绍及其使用
android·开发语言·c++·青少年编程·visual studio
liulilittle10 小时前
SNIProxy 轻量级匿名CDN代理架构与实现
开发语言·网络·c++·网关·架构·cdn·通信
tan77º10 小时前
【Linux网络编程】Socket - UDP
linux·服务器·网络·c++·udp