0

0

C++weak_ptr实现对象生命周期监控

P粉602998670

P粉602998670

发布时间:2025-09-04 11:43:24

|

607人浏览过

|

来源于php中文网

原创

weak_ptr通过lock()方法监控对象生命周期,解决循环引用和内存泄漏问题,在缓存、观察者模式、父子结构等场景中实现安全的非拥有性引用,确保对象销毁后访问可控。

c++weak_ptr实现对象生命周期监控

C++ weak_ptr
通过提供一种非拥有性引用,允许我们观察由
shared_ptr
管理的对象生命周期。当所有
shared_ptr
实例都销毁,对象被释放后,
weak_ptr
会“感知”到这一点,并在尝试访问时返回空,从而实现对对象存活状态的监控。这在需要打破循环引用或避免悬空指针风险的场景下,简直是设计模式中的一把利器。

解决方案

要实现

C++ weak_ptr
的对象生命周期监控,核心在于理解
weak_ptr
shared_ptr
之间的协作机制。
weak_ptr
本身不参与对象的引用计数,它只是一个“旁观者”。当你从一个
shared_ptr
创建一个
weak_ptr
时,它会指向同一个控制块,但不会增加引用计数。这意味着,即使有多个
weak_ptr
指向一个对象,只要所有
shared_ptr
都消失了,对象就会被销毁。

监控的关键在于

weak_ptr::lock()
方法。当你想要安全地访问
weak_ptr
所指向的对象时,应该调用
lock()
。这个方法会尝试创建一个
shared_ptr
。如果对象仍然存活(即至少有一个
shared_ptr
仍在管理它),
lock()
就会成功返回一个有效的
shared_ptr
,你可以通过这个
shared_ptr
安全地访问对象。如果对象已经被销毁(所有
shared_ptr
都已失效),
lock()
会返回一个空的
shared_ptr
。通过检查
lock()
返回的
shared_ptr
是否为空,我们就能准确判断对象是否还“活着”。

#include 
#include 
#include 

class MyObject {
public:
    std::string name;
    MyObject(const std::string& n) : name(n) {
        std::cout << "MyObject " << name << " created." << std::endl;
    }
    ~MyObject() {
        std::cout << "MyObject " << name << " destroyed." << std::endl;
    }
    void doSomething() {
        std::cout << "MyObject " << name << " is doing something." << std::endl;
    }
};

void monitor_object(std::weak_ptr weak_obj) {
    if (auto shared_obj = weak_obj.lock()) {
        std::cout << "  [Monitor] Object " << shared_obj->name << " is still alive." << std::endl;
        shared_obj->doSomething();
    } else {
        std::cout << "  [Monitor] Object has been destroyed." << std::endl;
    }
}

int main() {
    std::cout << "--- Phase 1: Object is alive ---" << std::endl;
    std::shared_ptr obj_ptr = std::make_shared("DataA");
    std::weak_ptr weak_obj_ptr = obj_ptr;

    monitor_object(weak_obj_ptr); // 此时对象存活

    std::cout << "\n--- Phase 2: Reset shared_ptr ---" << std::endl;
    obj_ptr.reset(); // 释放shared_ptr,对象被销毁

    monitor_object(weak_obj_ptr); // 此时对象已销毁

    std::cout << "\n--- Phase 3: Another object ---" << std::endl;
    {
        auto another_obj_ptr = std::make_shared("DataB");
        std::weak_ptr another_weak_ptr = another_obj_ptr;
        monitor_object(another_weak_ptr);
    } // another_obj_ptr 在这里离开作用域,对象被销毁
    monitor_object(std::weak_ptr(nullptr)); // 传递一个空的weak_ptr

    return 0;
}

这段代码清晰地展示了

weak_ptr
如何通过
lock()
方法来判断并安全访问一个对象。当
shared_ptr
还存在时,
lock()
能获取到有效的
shared_ptr
;一旦
shared_ptr
消失,对象被销毁,
lock()
就会返回空。

立即学习C++免费学习笔记(深入)”;

为什么
weak_ptr
能有效避免内存泄漏?

说白了,

weak_ptr
在避免内存泄漏方面的作用,主要体现在它能够漂亮地解决“循环引用”这个老大难问题。我们都知道,
shared_ptr
通过引用计数来管理对象的生命周期。当所有指向某个对象的
shared_ptr
都销毁了,引用计数归零,对象自然就被释放了。但想象一下,如果对象A持有一个指向对象B的
shared_ptr
,同时对象B又持有一个指向对象A的
shared_ptr
,这就像两个人互相抓着对方的衣领,谁也不肯放手。

在这种情况下,即使外部已经没有任何

shared_ptr
指向A或B,它们的内部引用计数却永远无法降到零。因为A的
shared_ptr
持有B,B的
shared_ptr
持有A,它们各自的引用计数至少为1。结果就是,A和B都无法被销毁,它们占用的内存也就永远无法释放,这就造成了经典的内存泄漏。

weak_ptr
的出现,就是为了打破这种僵局。它提供了一种“观察者”的角色,它指向对象,但不增加对象的引用计数。如果我们把循环引用中的一个
shared_ptr
替换成
weak_ptr
,比如让对象A持有对象B的
shared_ptr
,而对象B持有对象A的
weak_ptr
。这样一来,A的生命周期依然由B的
shared_ptr
影响(如果B是唯一的拥有者),但A的生命周期不再反过来影响B。当外部对A的
shared_ptr
全部失效时,A的引用计数归零,A就会被销毁。A销毁后,B持有的那个
weak_ptr
自然也就“过期”了,B就能感知到A已经不在了。这样一来,循环的链条就被斩断,两个对象都能在适当的时候被正确销毁,避免了内存泄漏。我个人觉得,这简直是处理复杂对象图关系时的救星。

在哪些实际场景中,
weak_ptr
的生命周期监控功能至关重要?

weak_ptr
的生命周期监控能力,在很多实际的、复杂的C++应用中都扮演着不可或缺的角色。它不仅仅是解决循环引用的工具,更是构建健壮、灵活系统的重要基石。

首先,缓存系统。想象一个图片缓存或者数据缓存,它存储了大量可能被程序其他部分使用的对象。如果缓存直接持有这些对象的

shared_ptr
,那么即使其他所有地方都不再使用某个图片,缓存也会因为持有
shared_ptr
而阻止该图片被释放。这会导致缓存无限增长,最终耗尽内存。正确的做法是,缓存应该持有
weak_ptr
。当其他部分不再需要某个图片时,它的
shared_ptr
就会被释放,图片对象自然销毁。缓存下次尝试访问时,通过
weak_ptr::lock()
发现图片已不存在,便可以安全地将其从缓存中移除,避免了存储已失效数据的开销。

其次,观察者模式或事件系统。在UI编程或事件驱动架构中,一个“主题”(Subject)可能需要通知多个“观察者”(Observer)某个事件发生了。如果主题直接持有观察者的

shared_ptr
,那么即使观察者本身已经不再需要,主题也会阻止它被销毁。这同样会导致内存泄漏。使用
weak_ptr
,主题可以安全地持有观察者的引用。当观察者自行销毁后,主题在遍历观察者列表时,通过
weak_ptr::lock()
就能发现哪些观察者已经失效,从而将其从通知列表中移除。这让观察者能够独立于主题管理自己的生命周期。

再者,父子或层级结构。例如,在游戏引擎的场景图或UI控件树中,父节点通常会拥有子节点的

shared_ptr
,这很合理。但子节点可能也需要访问它的父节点(例如,为了获取全局坐标或查找兄弟节点)。如果子节点也持有父节点的
shared_ptr
,那就又形成了循环引用。这时,子节点持有父节点的
weak_ptr
就是完美的解决方案。子节点可以通过
weak_ptr
安全地访问父节点,而不会影响父节点的生命周期。如果父节点被销毁了,子节点也能感知到,并做出相应处理。

Pixso AI
Pixso AI

Pixso AI是一款智能生成设计稿工具,通过AI一键实现文本输入到设计稿生成。

下载

最后,资源管理器。有时我们会有一些昂贵的资源(如数据库连接、网络套接字),它们可能被多个模块共享。一个中央的资源管理器可能持有这些资源的

shared_ptr
。而各个模块在使用这些资源时,如果只是需要临时访问而不希望延长其生命周期,就可以从资源管理器那里获取一个
weak_ptr
。这样,当资源管理器决定释放某个资源时(例如,因为长时间未使用或系统负载过高),各个模块的
weak_ptr
就会感知到资源已经失效,从而避免了对已释放资源的访问。

这些场景都突出了一点:

weak_ptr
提供了一种“软引用”机制,它允许我们建立引用关系,但这种关系不会阻止对象的销毁。这在设计复杂、动态的系统时,提供了极大的灵活性和安全性。

weak_ptr
shared_ptr
之间如何安全转换与协作?

weak_ptr
shared_ptr
的协作是C++智能指针设计哲学中一个非常精妙的部分,它提供了一种既能共享所有权又能安全观察的机制。它们之间的转换与协作,主要围绕着
lock()
方法展开。

首先,

weak_ptr
的创建。一个
weak_ptr
通常是从一个
shared_ptr
或者另一个
weak_ptr
构造出来的。例如:

std::shared_ptr s_ptr = std::make_shared("Initial");
std::weak_ptr w_ptr1 = s_ptr; // 从shared_ptr创建
std::weak_ptr w_ptr2 = w_ptr1; // 从另一个weak_ptr创建

需要注意的是,

weak_ptr
不能直接从原始指针或
unique_ptr
创建。它必须依附于一个已经由
shared_ptr
管理的资源。

其次,

weak_ptr
shared_ptr
的安全转换
。这是
weak_ptr
最核心的用法。由于
weak_ptr
不拥有对象,它指向的对象随时可能被销毁。因此,直接解引用
weak_ptr
是不安全的。为了安全地访问对象,我们必须将其“升级”为一个
shared_ptr
。这个过程是通过
lock()
方法完成的:

std::weak_ptr weak_obj_ptr = ...; // 假设已经有一个weak_ptr

if (auto shared_obj = weak_obj_ptr.lock()) {
    // 如果lock()成功,shared_obj现在是一个有效的shared_ptr
    // 并且在此作用域内,它会增加对象的引用计数,确保对象不会被销毁
    shared_obj->doSomething();
    std::cout << "Successfully locked and accessed object: " << shared_obj->name << std::endl;
} else {
    // lock()返回空,说明对象已经被销毁
    std::cout << "Failed to lock: object has been destroyed." << std::endl;
}

这里

lock()
方法的返回值是一个
shared_ptr
。如果对象仍然存活,它会返回一个指向该对象的
shared_ptr
,这会暂时增加对象的引用计数,保证在当前
shared_ptr
作用域内对象不会被意外销毁。如果对象已经销毁,
lock()
会返回一个空的
shared_ptr
。这种模式是使用
weak_ptr
进行生命周期监控的标准且安全的方式。

再次,

expired()
方法
weak_ptr
还有一个
expired()
方法,它返回一个布尔值,指示它所指向的对象是否已经被销毁。

if (weak_obj_ptr.expired()) {
    std::cout << "Object is already expired." << std::endl;
} else {
    std::cout << "Object is not expired yet." << std::endl;
}

然而,

expired()
方法本身并不提供线程安全性。也就是说,即使
expired()
返回
false
,在紧接着调用
lock()
之前,对象也可能在另一个线程中被销毁。因此,总是推荐使用
lock()
来尝试访问对象,而不是先用
expired()
判断再尝试访问。
lock()
是原子操作,它能确保在返回有效的
shared_ptr
时,对象是确实存在的。

最后,

weak_ptr
shared_ptr
的协作,本质上是所有权和观察者模式的结合。
shared_ptr
负责管理对象的生命周期和所有权,而
weak_ptr
则提供了一种轻量级的、非侵入式的观察机制,它允许你“窥视”一个对象是否仍然存在,而不会干扰它的销毁。这种分工使得在复杂系统中管理资源和对象关系变得更加清晰和安全。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

525

2023.08.10

空指针异常处理
空指针异常处理

本专题整合了空指针异常解决方法,阅读专题下面的文章了解更多详细内容。

22

2025.11.16

数据库三范式
数据库三范式

数据库三范式是一种设计规范,用于规范化关系型数据库中的数据结构,它通过消除冗余数据、提高数据库性能和数据一致性,提供了一种有效的数据库设计方法。本专题提供数据库三范式相关的文章、下载和课程。

360

2023.06.29

如何删除数据库
如何删除数据库

删除数据库是指在MySQL中完全移除一个数据库及其所包含的所有数据和结构,作用包括:1、释放存储空间;2、确保数据的安全性;3、提高数据库的整体性能,加速查询和操作的执行速度。尽管删除数据库具有一些好处,但在执行任何删除操作之前,务必谨慎操作,并备份重要的数据。删除数据库将永久性地删除所有相关数据和结构,无法回滚。

2083

2023.08.14

vb怎么连接数据库
vb怎么连接数据库

在VB中,连接数据库通常使用ADO(ActiveX 数据对象)或 DAO(Data Access Objects)这两个技术来实现:1、引入ADO库;2、创建ADO连接对象;3、配置连接字符串;4、打开连接;5、执行SQL语句;6、处理查询结果;7、关闭连接即可。

349

2023.08.31

MySQL恢复数据库
MySQL恢复数据库

MySQL恢复数据库的方法有使用物理备份恢复、使用逻辑备份恢复、使用二进制日志恢复和使用数据库复制进行恢复等。本专题为大家提供MySQL数据库相关的文章、下载、课程内容,供大家免费下载体验。

256

2023.09.05

vb中怎么连接access数据库
vb中怎么连接access数据库

vb中连接access数据库的步骤包括引用必要的命名空间、创建连接字符串、创建连接对象、打开连接、执行SQL语句和关闭连接。本专题为大家提供连接access数据库相关的文章、下载、课程内容,供大家免费下载体验。

326

2023.10.09

数据库对象名无效怎么解决
数据库对象名无效怎么解决

数据库对象名无效解决办法:1、检查使用的对象名是否正确,确保没有拼写错误;2、检查数据库中是否已存在具有相同名称的对象,如果是,请更改对象名为一个不同的名称,然后重新创建;3、确保在连接数据库时使用了正确的用户名、密码和数据库名称;4、尝试重启数据库服务,然后再次尝试创建或使用对象;5、尝试更新驱动程序,然后再次尝试创建或使用对象。

413

2023.10.16

2026赚钱平台入口大全
2026赚钱平台入口大全

2026年最新赚钱平台入口汇总,涵盖任务众包、内容创作、电商运营、技能变现等多类正规渠道,助你轻松开启副业增收之路。阅读专题下面的文章了解更多详细内容。

54

2026.01.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C# 教程
C# 教程

共94课时 | 8.1万人学习

C 教程
C 教程

共75课时 | 4.3万人学习

C++教程
C++教程

共115课时 | 15万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号