当前位置: 代码网 > it编程>编程语言>C/C++ > C++中智能指针weak_ptr的原理及使用

C++中智能指针weak_ptr的原理及使用

2025年06月13日 C/C++ 我要评论
1. weak_ptr 的基本概念weak_ptr是 c++11 引入的一种智能指针,它与shared_ptr配合使用,主要解决以下问题:打破循环引用:防止shared_ptr之间的循环引用导致内存泄

1. weak_ptr 的基本概念

weak_ptr 是 c++11 引入的一种智能指针,它与 shared_ptr 配合使用,主要解决以下问题:

  • 打破循环引用:防止 shared_ptr 之间的循环引用导致内存泄漏
  • 安全观察:允许观察共享对象而不影响其生命周期
  • 避免悬空指针:可以检测被观察对象是否已被释放

2. weak_ptr 的核心原理

2.1 控制块结构

weak_ptr 与 shared_ptr 共享同一个控制块,控制块包含:

struct controlblock {
    std::atomic<size_t> shared_count;  // 强引用计数
    std::atomic<size_t> weak_count;    // 弱引用计数
    // 其他元数据(删除器、分配器等)
};

2.2 内存管理规则

  • 对象销毁条件:当 shared_count 归零时,管理对象被销毁
  • 控制块销毁条件:当 shared_count 和 weak_count 都归零时,控制块被释放
  • weak_ptr 不参与所有权:仅增加 weak_count,不影响 shared_count

2.3 工作流程

// 创建 shared_ptr(控制块:shared=1, weak=0)
auto sp = std::make_shared<int>(42);

// 创建 weak_ptr(控制块:shared=1, weak=1)
std::weak_ptr<int> wp = sp;

// shared_ptr 析构(控制块:shared=0, weak=1)
sp.reset();

// 此时:
// - 管理的 int 对象已被销毁
// - 控制块仍然存在(weak_count=1)
// - wp.expired() == true

// weak_ptr 析构(控制块:shared=0, weak=0)
// 控制块被释放

3. weak_ptr 的关键操作

3.1 创建与赋值

// 从 shared_ptr 创建
auto sp = std::make_shared<myclass>();
std::weak_ptr<myclass> wp1(sp);

// 拷贝构造
std::weak_ptr<myclass> wp2(wp1);

// 赋值操作
std::weak_ptr<myclass> wp3;
wp3 = wp1;

3.2 检查与升级

// 检查对象是否有效
if (!wp.expired()) {
    // 尝试升级为 shared_ptr
    if (auto sp = wp.lock()) {
        // 使用 sp 安全访问对象
    }
}

3.3 资源释放监控

// 监控资源释放
std::weak_ptr<myclass> wp;

{
    auto sp = std::make_shared<myclass>();
    wp = sp;
    // 对象存在
}

// 此时 wp.expired() == true

4. weak_ptr 的线程安全性

4.1 官方标准规定

  • 控制块操作是原子的weak_count 的增减是线程安全的
  • lock() 操作是线程安全的:升级为 shared_ptr 的过程是原子的
  • 对象访问需要同步:通过 lock() 获取的 shared_ptr 需要额外同步

4.2 线程安全示例

std::shared_ptr<int> sp = std::make_shared<int>(42);
std::weak_ptr<int> wp(sp);

// 线程1
if (auto local_sp = wp.lock()) {
    std::lock_guard<std::mutex> lock(mtx);
    *local_sp = 10;
}

// 线程2
if (auto local_sp = wp.lock()) {
    std::lock_guard<std::mutex> lock(mtx);
    int val = *local_sp;
}

5. weak_ptr 的典型应用场景

5.1 打破循环引用

class parent {
    std::shared_ptr<child> child;
};

class child {
    std::weak_ptr<parent> parent;  // 使用 weak_ptr 避免循环
};

5.2 缓存系统

class cache {
    std::unordered_map<key, std::weak_ptr<resource>> cache;
    
    std::shared_ptr<resource> get(key key) {
        if (auto it = cache.find(key); it != cache.end()) {
            if (auto sp = it->second.lock()) {
                return sp;  // 缓存命中
            }
            cache.erase(it);  // 清理过期缓存
        }
        // 缓存未命中,创建新资源
        auto sp = std::make_shared<resource>(key);
        cache[key] = sp;
        return sp;
    }
};

5.3 观察者模式

class subject {
    std::vector<std::weak_ptr<observer>> observers;
    
    void notify() {
        for (auto it = observers.begin(); it != observers.end(); ) {
            if (auto obs = it->lock()) {
                obs->update();
                ++it;
            } else {
                it = observers.erase(it);  // 移除无效观察者
            }
        }
    }
};

6. weak_ptr 的实现细节

6.1 控制块生命周期

6.2 lock() 的原子实现

lock() 操作必须保证线程安全,伪代码实现:

shared_ptr<t> lock() const noexcept {
    controlblock* cb = get_control_block();
    size_t sc = cb->shared_count.load();
    do {
        if (sc == 0) return nullptr;  // 对象已释放
        // 尝试增加 shared_count(cas操作)
    } while (!cb->shared_count.compare_exchange_weak(sc, sc + 1));
    
    return shared_ptr<t>(cb);  // 创建新的 shared_ptr
}

7. weak_ptr 的注意事项

不能直接解引用:必须先用 lock() 升级为 shared_ptr

// 错误用法
// *wp;  // 编译错误

// 正确用法
if (auto sp = wp.lock()) {
    *sp = value;
}

性能考虑

  • lock() 操作包含原子操作,有一定开销
  • 控制块需要额外内存(通常16-32字节)

构造函数限制

// 不能直接从裸指针构造
// std::weak_ptr<int> wp(new int(42));  // 错误

// 必须从 shared_ptr 构造
auto sp = std::make_shared<int>(42);
std::weak_ptr<int> wp(sp);  // 正确

8. 性能优化建议

避免频繁 lock/unlock:在需要时缓存 shared_ptr

void process(std::weak_ptr<data> wp) {
    auto sp = wp.lock();  // 只调用一次 lock
    if (!sp) return;
    
    // 多次使用 sp 而不重复调用 lock
    sp->operation1();
    sp->operation2();
}

及时清理失效 weak_ptr:定期检查并移除 expired() 的 weak_ptr

考虑使用 make_shared:对象和控制块单次分配,减少内存碎片

9. 与其它智能指针的对比

特性weak_ptrshared_ptrunique_ptr
所有权共享独占
影响生命周期
直接访问对象需通过 lock()可直接访问可直接访问
引用计数只增加 weak_count增加 shared_count
典型用途打破循环引用/观察共享所有权独占所有权

10. 现代 c++ 中的增强

10.1 c++17 的 weak_from_this

class myclass : public std::enable_shared_from_this<myclass> {
public:
    std::weak_ptr<myclass> get_weak() {
        return weak_from_this();  // 安全获取 weak_ptr
    }
};

auto obj = std::make_shared<myclass>();
auto wobj = obj->get_weak();  // 安全获取 weak_ptr

10.2 c++20 的原子 shared_ptr/weak_ptr

std::atomic<std::weak_ptr<int>> atomic_wp;
atomic_wp.store(wp, std::memory_order_release);
auto current = atomic_wp.load(std::memory_order_acquire);

11. 总结

weak_ptr 的核心原理可以总结为:

  • 不参与所有权:仅观察不拥有,不影响对象生命周期
  • 共享控制块:与 shared_ptr 共享同一控制块,维护 weak_count
  • 安全升级:通过 lock() 原子操作获取可用的 shared_ptr
  • 自动清理:当最后一个 weak_ptr 析构后,控制块被释放

正确使用 weak_ptr 可以:

  • 有效解决循环引用导致的内存泄漏
  • 实现安全的对象观察模式
  • 构建高效的缓存系统
  • 开发更健壮的异步代码

理解 weak_ptr 的工作原理对于设计复杂的内存管理系统和避免资源泄漏至关重要,它是现代 c++ 高效内存管理工具链中不可或缺的一环。

到此这篇关于c++中智能指针weak_ptr的原理及使用的文章就介绍到这了,更多相关c++ weak_ptr内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com