首页 > 解决方案 > 共享对象工厂 - 更新 weak_ptr 映射

问题描述

我有一个场景,许多对象可能需要加载一些资源,但是当资源的路径相同时,应该共享资源对象。以下简化代码呈现了上述概念:

struct Obj
{
};

struct SharedObjects
{
    std::shared_ptr<Obj> CreateObj(const std::string& key)
    {
        auto itr = registry_.find(key);
        if(itr != registry_.end())
        {
            return itr->second.lock();
        }
        auto obj = std::make_shared<Obj>();
        registry_[key] = obj;
        return obj;
    }

    void ObjRemoved(const std::string& key)
    {
        auto itr = registry_.find(key);
        if(itr != registry_.end())
        {
            if(!itr->second.use_count())
            {
                registry_.erase(itr);
            }
        }
    }
private:
    std::unordered_map<std::string, std::weak_ptr<Obj>> registry_;
};

int main()
{
    SharedObjects sharedObjects;
    auto p1 = sharedObjects.CreateObj("a");
    auto p2 = sharedObjects.CreateObj("b");
    auto p3 = sharedObjects.CreateObj("a");

    p2.reset();
    sharedObjects.ObjRemoved("b");
}

我想知道如何改进更新 weak_ptr 地图?正如你现在看到的,我调用了 reset 和 ObjRemoved 函数。你有什么想法 ?(也许我应该使用一些设计模式)。您在代码中看到任何其他可以改进的地方吗?

Edit1 这是可能的解决方案:

可以从 Obj dtor 中删除空的 weak_ptr:

struct Obj
{
    Obj::Obj(std::function<void()> clb): clb_(std::move(clb))
    {}
    Obj::~Obj()
    {
        clb_();
    }

private:
    std::function<void()> clb_;
};

修改 CreateObj 函数(ObjRemoved 函数被移除):

std::shared_ptr<Obj> SharedObjects::CreateObj(const std::string& key)
{
    auto itr = registry_.find(key);

    if(itr != registry_.end())
    {
        return itr->second.lock();
    }

   auto obj = std::make_shared<Obj>([this, key]
   {
       registry_.erase(key);
   });

   registry_[key] = obj;
   return obj;
}

Edit2 没有智能指针的替代解决方案:

struct Obj
{
};

struct SharedObj
{
    Obj* CreateObj(const std::string& key)
    {
        auto itr = registry.find(key);
        if(itr != registry.end())
        {
            ++(itr->second.refCount);
            return &itr->second.obj;
        }

        auto& objRecord = registry[key] = ObjRecord{};
        return &objRecord.obj;
    }

    void RemoveObj(const std::string& key)
    {
        auto itr = registry.find(key);
        if (itr != registry.end())
        {
            auto& refCount = itr->second.refCount;
            --refCount;

            if (refCount == 0)
            {
                registry.erase(itr);
            }
        }
    }
private:
    struct ObjRecord
    {
        Obj obj;
        std::uint16_t refCount{1};
    };
    std::unordered_map<std::string, ObjRecord> registry;
};

标签: c++c++17

解决方案


推荐阅读