跳转到内容

C++ weak ptr 详解

来自代码酷

C++ weak_ptr详解[编辑 | 编辑源代码]

介绍[编辑 | 编辑源代码]

weak_ptr 是 C++11 引入的智能指针之一,用于解决 shared_ptr 的循环引用问题。它不拥有对象的所有权,而是观察一个由 shared_ptr 管理的对象。当最后一个 shared_ptr 被销毁时,weak_ptr 会自动感知到目标对象已被释放,避免了悬挂指针的风险。

weak_ptr 的主要特点:

  • 不增加引用计数
  • 必须通过 lock() 方法转换为 shared_ptr 才能访问对象
  • 常用于打破 shared_ptr 的循环引用

基本用法[编辑 | 编辑源代码]

创建 weak_ptr[编辑 | 编辑源代码]

weak_ptr 通常通过 shared_ptr 构造或赋值:

#include <memory>
#include <iostream>

int main() {
    std::shared_ptr<int> shared = std::make_shared<int>(42);
    std::weak_ptr<int> weak(shared);  // 从shared_ptr构造
    
    // 检查weak_ptr是否有效
    if (!weak.expired()) {
        std::cout << "weak_ptr is observing a valid object\n";
    }
    
    return 0;
}

输出:

weak_ptr is observing a valid object

访问对象[编辑 | 编辑源代码]

weak_ptr 不能直接访问对象,必须通过 lock() 方法获取一个临时的 shared_ptr:

#include <memory>
#include <iostream>

int main() {
    std::shared_ptr<int> shared = std::make_shared<int>(100);
    std::weak_ptr<int> weak = shared;
    
    if (auto tempShared = weak.lock()) {  // 转换为shared_ptr
        std::cout << "Value: " << *tempShared << "\n";
    } else {
        std::cout << "Object no longer exists\n";
    }
    
    shared.reset();  // 释放对象
    
    if (weak.expired()) {
        std::cout << "Object has been deleted\n";
    }
    
    return 0;
}

输出:

Value: 100
Object has been deleted

循环引用问题[编辑 | 编辑源代码]

weak_ptr 最常见的用途是解决 shared_ptr 的循环引用问题。考虑以下场景:

graph LR A[shared_ptr A] --> B[Object B] B --> C[shared_ptr C] C --> A

问题示例[编辑 | 编辑源代码]

#include <memory>
#include <iostream>

struct Node {
    std::shared_ptr<Node> next;
    ~Node() { std::cout << "Node destroyed\n"; }
};

int main() {
    auto node1 = std::make_shared<Node>();
    auto node2 = std::make_shared<Node>();
    
    node1->next = node2;
    node2->next = node1;  // 循环引用
    
    return 0;  // 对象不会被销毁!
}

解决方案[编辑 | 编辑源代码]

使用 weak_ptr 打破循环:

#include <memory>
#include <iostream>

struct Node {
    std::weak_ptr<Node> next;  // 改为weak_ptr
    ~Node() { std::cout << "Node destroyed\n"; }
};

int main() {
    auto node1 = std::make_shared<Node>();
    auto node2 = std::make_shared<Node>();
    
    node1->next = node2;
    node2->next = node1;  // weak_ptr不会增加引用计数
    
    return 0;  // 对象会被正确销毁
}

输出:

Node destroyed
Node destroyed

实际应用场景[编辑 | 编辑源代码]

缓存系统[编辑 | 编辑源代码]

weak_ptr 非常适合实现对象缓存,当外部不再需要对象时,缓存可以自动释放:

#include <memory>
#include <unordered_map>
#include <iostream>

class Cache {
    std::unordered_map<int, std::weak_ptr<std::string>> cache;
    
public:
    std::shared_ptr<std::string> get(int id) {
        auto it = cache.find(id);
        if (it != cache.end()) {
            return it->second.lock();  // 返回shared_ptr或nullptr
        }
        return nullptr;
    }
    
    void store(int id, std::shared_ptr<std::string> value) {
        cache[id] = value;
    }
};

int main() {
    Cache cache;
    
    {
        auto data = std::make_shared<std::string>("Important Data");
        cache.store(1, data);
        
        if (auto cached = cache.get(1)) {
            std::cout << *cached << "\n";  // 输出: Important Data
        }
    }  // data离开作用域,被销毁
    
    if (auto cached = cache.get(1)) {
        std::cout << "Data still exists\n";
    } else {
        std::cout << "Data has been released\n";  // 会执行这一行
    }
    
    return 0;
}

观察者模式[编辑 | 编辑源代码]

weak_ptr 可用于实现观察者模式,避免观察者意外延长被观察对象的生命周期:

#include <memory>
#include <vector>
#include <iostream>

class Subject;

class Observer {
public:
    virtual void update() = 0;
    virtual ~Observer() = default;
};

class Subject {
    std::vector<std::weak_ptr<Observer>> observers;
    
public:
    void attach(std::weak_ptr<Observer> obs) {
        observers.push_back(obs);
    }
    
    void notify() {
        for (auto& weak_obs : observers) {
            if (auto obs = weak_obs.lock()) {
                obs->update();
            }
        }
    }
};

class ConcreteObserver : public Observer {
public:
    void update() override {
        std::cout << "Observer notified!\n";
    }
};

int main() {
    auto subject = std::make_shared<Subject>();
    auto observer = std::make_shared<ConcreteObserver>();
    
    subject->attach(observer);
    subject->notify();  // 输出: Observer notified!
    
    return 0;
}

性能考虑[编辑 | 编辑源代码]

  • weak_ptr 的操作(如 lock())比 shared_ptr 稍慢,因为需要检查引用计数
  • 每个 weak_ptr 会增加少量内存开销(控制块中需要维护 weak count)
  • 在性能关键路径中应谨慎使用

数学原理[编辑 | 编辑源代码]

weak_ptr 的引用计数机制可以用以下公式表示:

解析失败 (未知函数“\begin{cases}”): {\displaystyle \begin{cases} \text{对象存活} & \text{当且仅当} \quad \text{shared\_count} > 0 \\ \text{weak\_count} & \text{表示有多少 weak\_ptr 正在观察该对象} \end{cases} }

即使 shared_count 降为 0,只要 weak_count > 0,控制块仍会保留,直到 weak_count 也降为 0。

最佳实践[编辑 | 编辑源代码]

1. 优先使用 shared_ptr 和 unique_ptr,只在必要时使用 weak_ptr 2. 每次访问 weak_ptr 前都应检查是否 expired() 或验证 lock() 的返回值 3. 避免长期持有 weak_ptr,这可能导致控制块无法释放 4. 在多线程环境中使用时,lock() 操作是线程安全的

总结[编辑 | 编辑源代码]

weak_ptr 是 C++ 智能指针家族中的重要成员,它:

  • 解决了 shared_ptr 的循环引用问题
  • 实现了非拥有式观察语义
  • 广泛应用于缓存、观察者模式等场景
  • 需要配合 lock() 方法安全访问对象

正确使用 weak_ptr 可以显著提高 C++ 程序的资源管理能力和安全性。