You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++中shared_ptr多容器存储后的同步删除问题咨询

关于多容器中shared_ptr的同步删除问题

先看你提供的这段使用shared_ptr管理对象的代码:

struct D { 
    D() { std::cout << "D ctor\n"; } 
    ~D() { std::cout << "D dtor\n"; } 
}; 

struct CC { 
private: 
    std::vector<std::shared_ptr<D>> pDs; 
    static std::shared_ptr<CC> pC; 
    CC() { std::cout << "C ctor\n"; }; 
public: 
    static std::shared_ptr<CC> GetInstance() { 
        if (nullptr == pC) { 
            pC = std::shared_ptr<CC>(new CC); 
            return pC; 
        } 
        return pC; 
    } 
    void Save(std::shared_ptr<D> d) { 
        pDs.emplace_back(d); 
        std::cout << "saved b\n"; 
    } 
    void Delete(int i) { 
        pDs.erase(pDs.begin() + i); 
    } 
    int Size() { return pDs.size(); } 
    ~CC() { std::cout << "C dtor\n"; } 
}; 

std::shared_ptr<CC> CC::pC(nullptr); 

struct Caller { 
    void Init() { 
        std::shared_ptr<D> d = std::shared_ptr<D>(new D()); 
        CC::GetInstance()->Save(d); 
    } 
    int Size() { return CC::GetInstance()->Size(); } 
}; 

struct Deletor { 
    void Delete(int i) { CC::GetInstance()->Delete(i); } 
};

接下来针对你提出的问题:当多个容器(比如MAP_A和MAP_B)存储同一个shared_ptr实例,删除其中一个容器里的元素时,该如何处理其他容器中对应的实例,我分两种场景来解答:


核心原理先明确

shared_ptr的核心是引用计数:每个shared_ptr都是对原始对象的一个引用,当你把同一个shared_ptr(或通过拷贝构造/赋值得到的新shared_ptr)存入多个容器时,这些shared_ptr都会共享同一个引用计数。

当你调用MAP_A.erase(key_i)时,只是把MAP_A中对应的shared_ptr销毁,引用计数会减1,但只要MAP_B里还有对应的shared_ptr,引用计数就不会降到0,原始对象也不会被销毁。


场景1:只关心对象的自动销毁,不需要同步删除其他容器的元素

这种情况你完全不需要做额外处理。shared_ptr的引用计数机制会自动帮你管理对象生命周期:只有当所有容器里的对应shared_ptr都被erase(或者被销毁),引用计数降到0时,原始对象才会自动调用析构函数销毁。

比如你的示例中:

std::map<int, std::shared_ptr<D>> MAP_A, MAP_B;
auto d = std::make_shared<D>();
MAP_A[1] = d;
MAP_B[1] = d;

MAP_A.erase(1); // 此时d的引用计数从2降到1,对象不会销毁
// MAP_B里的d仍然有效,可以正常使用
MAP_B.erase(1); // 引用计数降到0,对象销毁,调用D的析构函数

场景2:需要同步删除所有容器中对应的元素(即删除一个容器的项,其他容器的对应项也要删掉)

这种需求shared_ptr本身做不到,因为它只负责对象的生命周期管理,不负责跟踪自己被哪些容器存储。你需要额外的机制来实现同步:

方案1:维护一个全局的反向映射表

创建一个结构,记录每个shared_ptr对应的所有容器的信息(比如容器类型、键值)。当你要删除某个容器中的元素时,先查这个反向表,找到所有存储该shared_ptr的容器,然后逐个调用erase。

举个简单的例子:

#include <map>
#include <vector>
#include <memory>
#include <unordered_set>

// 假设我们用int作为键,存储shared_ptr<D>的容器有map和vector
struct ContainerTracker {
    // 记录每个shared_ptr<D>对应的所有map的键
    std::unordered_map<std::shared_ptr<D>, std::unordered_set<int>> mapKeys;
    // 记录每个shared_ptr<D>对应的所有vector的索引
    std::unordered_map<std::shared_ptr<D>, std::unordered_set<size_t>> vectorIndices;

    // 维护所有需要同步的map和vector
    std::vector<std::map<int, std::shared_ptr<D>>*> allMaps;
    std::vector<std::vector<std::shared_ptr<D>>*> allVectors;

    // 向map中添加元素时,同时更新跟踪表
    void addToMap(std::map<int, std::shared_ptr<D>>& map, int key, std::shared_ptr<D> d) {
        map[key] = d;
        mapKeys[d].insert(key);
        // 如果这个map还没被加入跟踪列表,就添加进去
        if (std::find(allMaps.begin(), allMaps.end(), &map) == allMaps.end()) {
            allMaps.push_back(&map);
        }
    }

    // 删除map中的元素时,同步删除其他容器的对应元素
    void eraseFromMap(std::map<int, std::shared_ptr<D>>& map, int key) {
        auto it = map.find(key);
        if (it == map.end()) return;
        auto d = it->second;
        map.erase(it);

        // 同步删除其他map中的对应键
        for (auto* otherMap : allMaps) {
            if (otherMap == &map) continue;
            for (int k : mapKeys[d]) {
                otherMap->erase(k);
            }
        }
        // 同步删除vector中的对应索引(注意:vector删除元素后索引会变化,这里只是简化示例)
        for (auto* vec : allVectors) {
            std::vector<size_t> validIndices;
            for (size_t idx : vectorIndices[d]) {
                if (idx < vec->size()) {
                    validIndices.push_back(idx);
                }
            }
            // 倒序删除避免索引失效
            std::sort(validIndices.rbegin(), validIndices.rend());
            for (size_t idx : validIndices) {
                vec->erase(vec->begin() + idx);
            }
        }

        // 从跟踪表中移除该shared_ptr的记录
        mapKeys.erase(d);
        vectorIndices.erase(d);
    }
};

注意:这个方案需要你严格通过跟踪表的接口来操作所有容器,否则会出现不同步的情况。

方案2:使用观察者模式

让每个容器成为观察者,当某个容器删除元素时,通知所有其他容器删除对应的元素。或者创建一个统一的管理类,所有容器的添加/删除操作都通过这个类执行,由它负责同步所有容器的状态。


总结

  • 如果只是要保证对象自动销毁,不用做任何额外操作,shared_ptr的引用计数会自动处理。
  • 如果需要同步删除所有容器中的对应元素,需要自己实现额外的跟踪机制,比如反向映射表或者观察者模式,确保所有容器的操作都被统一管理。

内容的提问来源于stack exchange,提问作者Hello Everyone

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 04:18:58