首页 > 后端开发 > C++ > 正文

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

P粉602998670
发布: 2025-09-04 11:43:24
原创
598人浏览过
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 <iostream>
#include <memory>
#include <string>

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<MyObject> 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<MyObject> obj_ptr = std::make_shared<MyObject>("DataA");
    std::weak_ptr<MyObject> 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<MyObject>("DataB");
        std::weak_ptr<MyObject> another_weak_ptr = another_obj_ptr;
        monitor_object(another_weak_ptr);
    } // another_obj_ptr 在这里离开作用域,对象被销毁
    monitor_object(std::weak_ptr<MyObject>(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
登录后复制
安全地访问父节点,而不会影响父节点的生命周期。如果父节点被销毁了,子节点也能感知到,并做出相应处理。

WeShop唯象
WeShop唯象

WeShop唯象是国内首款AI商拍工具,专注电商产品图片的智能生成。

WeShop唯象113
查看详情 WeShop唯象

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

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<MyObject> s_ptr = std::make_shared<MyObject>("Initial");
std::weak_ptr<MyObject> w_ptr1 = s_ptr; // 从shared_ptr创建
std::weak_ptr<MyObject> 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<MyObject> 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
登录后复制
则提供了一种轻量级的、非侵入式的观察机制,它允许你“窥视”一个对象是否仍然存在,而不会干扰它的销毁。这种分工使得在复杂系统中管理资源和对象关系变得更加清晰和安全。

以上就是C++weak_ptr实现对象生命周期监控的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习
PHP中文网抖音号
发现有趣的

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