返回题库

weak_ptr 缓存模式

Weak Ptr Cache

专题
Systems & Architecture / 系统与架构
难度
L2
来源
MyntBit

题目详情

std::weak_ptr 可以观测 shared_ptr 管理的对象而不延长其生命周期。在缓存场景中,使用 weak_ptr 允许缓存在对象不再被其他地方引用时自动清理条目,避免内存泄漏和悬垂指针。

任务:实现一个基于 weak_ptr 的自动清理缓存容器。

英文原题

In high-frequency trading systems, maintaining an efficient cache of market data snapshots is critical for performance and memory management. Utilizing weak pointers allows multiple components, such as pricing engines and risk monitors, to share snapshot instances without redundant allocations while ensuring automatic eviction when no active references remain.
Task
Implement a MarketDataCache class utilizing std::weak_ptr to manage shared Snapshot objects.
The class must support the following m

解析

问题分析

std::weak_ptr 可以观测 shared_ptr 管理的对象而不延长其生命周期。在缓存场景中,使用 weak_ptr 允许缓存在对象不再被其他地方引用时自动清理条目,避免内存泄漏和悬垂指针。

实现

template<typename K, typename V>
class WeakPtrCache {
    std::unordered_map<K, std::weak_ptr<V>> cache_;
    std::mutex mtx_;
public:
    void put(const K& key, std::shared_ptr<V> value) {
        std::lock_guard lk(mtx_);
        cache_[key] = value;
    }
    std::shared_ptr<V> get(const K& key) {
        std::lock_guard lk(mtx_);
        auto it = cache_.find(key);
        if (it == cache_.end()) return nullptr;
        auto sp = it->second.lock();
        if (!sp) cache_.erase(it); // 惰性清理
        return sp;
    }
    void cleanup() {
        std::lock_guard lk(mtx_);
        for (auto it = cache_.begin(); it != cache_.end(); )
            if (it->second.expired()) it = cache_.erase(it); else ++it;
    }
};

复杂度与边界

  • 时间复杂度:get/put O(1),cleanup O(N)
  • 空间复杂度:O(缓存条目数)
  • 边界条件:(1) 缓存条目过期后 get 返回 nullptr (2) 并发 get/put 需互斥锁 (3) cleanup 可后台定期执行而非每次 get

英文解析

Analysis

`std::weak_ptr` can observe objects managed by `shared_ptr` without extending their lifetime. In caching scenarios, using `weak_ptr` allows cache entries to auto-clean when objects are no longer referenced elsewhere, avoiding memory leaks and dangling pointers.

Solution

template<typename K, typename V>
class WeakPtrCache {
    std::unordered_map<K, std::weak_ptr<V>> cache_;
    std::mutex mtx_;
public:
    void put(const K& key, std::shared_ptr<V> value) {
        std::lock_guard lk(mtx_);
        cache_[key] = value;
    }
    std::shared_ptr<V> get(const K& key) {
        std::lock_guard lk(mtx_);
        auto it = cache_.find(key);
        if (it == cache_.end()) return nullptr;
        auto sp = it->second.lock();
        if (!sp) cache_.erase(it); // lazy cleanup
        return sp;
    }
    void cleanup() {
        std::lock_guard lk(mtx_);
        for (auto it = cache_.begin(); it != cache_.end(); )
            if (it->second.expired()) it = cache_.erase(it); else ++it;
    }
};

Complexity & Edge Cases

  • Time complexity: get/put O(1), cleanup O(N)
  • Space complexity: O(cache entries)
  • Edge cases: (1) Expired cache entries return nullptr on get (2) Concurrent get/put requires mutex (3) cleanup can run periodically in background rather than on every get

Key Considerations

  1. Cache eviction granularity: weak_ptr expires when shared_ptr count drops to zero; cache entry disappears without explicit eviction — suitable for warm caches, not persistent storage
  2. Lock contention: Concurrent cache access requires mutex around the weak_ptr lookup; lock scope must be minimal (check + promote, not full scan)
  3. Promotion failure: weak_ptr::lock() returns null if object was destroyed; caller must handle null gracefully, typically by reconstructing the object
  4. Memory overhead: Cache stores weak_ptr control block references, not objects; overhead is minimal per entry but control blocks themselves consume memory