在对象之间拥有或共享资源

时间:2018-08-26 10:35:31

标签: c++ shared-ptr c++17

A具有以下类型的工作程序对象的集合:

template<class T> struct Worker;

具有不同模板参数T的不同工作人员会使用一些资源

struct Resource;
不同的Resource使用的

Worker都具有相同的类型,但可能具有不同的值。通常,资源很繁重,应避免不必要的副本。资源仅由工作人员使用,一些资源专用,一些资源在它们之间共享(此信息在编译时可用)。如果专门使用资源,则我希望Worker是它的所有者(可能是有效的),否则应该存储一个引用。用伪代码,我想要么做

Resource resource1(...);
Worker<Type1> worker1(..., std::move(resource1));
// worker1 owns resource1

Resource resource2(...);
Worker<Type2> worker2(..., std::move(resource2));
// worker2 owns resource2

Resource resource(...);
Worker<Type1> worker1(..., resource);
Worker<Type2> worker2(..., resource);
// workers use resource, but do not own it

我看到了两种直接的方法:

  1. 使用模板参数来确定Worker中代表资源的数据成员的类型:

    template<class T, class R>
    struct Worker
    {
        Worker(T arg, R resource) :
            resource_(std::forward<R>(resource))
        { }  
    
        R resource_;
    };
    
    template<class T, class R>
    Worker(T arg, R&& resource) -> Worker<T, R>;
    
    void foo()
    {
        Resource r;
    
        Worker worker1(0, r);
        Worker worker2(0, r);
        // type of worker1.resource_ is Resource&,
        // type of worker2.resource_ is Resource&,
        // both store a reference to r
    
        Worker worker3(0, Resource{});
        // type of worker3.resource_ is Resource,
        // Resource{} is moved into it
    }
    
  2. 使用std::shared_ptr

    template<class T>
    struct Worker
    {
        Worker(T arg, const std::shared_ptr<Resource>& resource) :
            resource_(resource)
        { }
    
        template<class R, typename = std::enable_if_t<std::is_same_v<R, Resource>>>
        Worker(T arg, R&& resource) :
            resource_(std::make_shared<Resource>(std::move(resource)))
        { }
    
        std::shared_ptr<Resource> resource_;
    };
    
    void foo()
    {
        auto resource = std::make_shared<Resource>();
    
        Worker worker1(0, resource);
        Worker worker2(0, resource);
        // worker1 and worker2 share *resource_
    
        Worker worker3(0, Resource{});
        // worker3 effectively owns *resource_
    }
    

这些方法显然有很大不同,但在当前情况下实际上是等效的。我都不喜欢他们。

在第一种情况下,我不喜欢将资源存储在Worker之外的某个局部变量中。在Worker中悬挂参考存在潜在的风险。

在第二种情况下,在创建所有resource之后,不必要的计数引用仍保留在Worker中。我可以按值取shared_ptr并在最后的std::move结构中使用Worker,但这需要我显式地跟踪最后一个构造函数-这是获取错误的绝佳方法。

这些问题不会影响代码的正确性,但可能表示整个设计存在缺陷。我认为应该有更好的方法。请说明在这种情况下的最佳做法。 (我知道我的问题可能太笼统了,我需要的解决方案可能取决于我创建资源和工作人员的确切方式;好的解决方案可能是由对该问题的不同看法导致的,目前,我陷入了两种方法中我在上面概述了。)

Full code here

1 个答案:

答案 0 :(得分:0)

  

在第二种情况下,不必要的计数参考保留在资源中   在所有工人被创建之后。我可以按价值来估算shared_ptr   并在上一个Worker构造中使用std::move,但是它将   要求我明确跟踪最后一个构造函数-一种极好的方法   得到一个错误。

不,您不会;默认情况下,使用unique_ptr创建资源,在需要时将其转换为shared_ptr无需花费任何费用。


仅考虑使用按值接受std::shared_ptr 的构造函数; std::shared_ptr的构造函数采用 rvalue std::unique_ptr;

template<class T>
struct Worker
{
    Worker(T arg, std::shared_ptr<Resource> resource) :
        resource_(std::move(resource))
    { }

    std::shared_ptr<Resource> resource_;
};

void foo()
{
    auto resource = std::make_unique<Resource>();
    auto resource_shared = std::make_shared<Resource>();

    Worker worker1(0, std::move(resource));
    Worker worker2(0, resource_shared);
    Worker worker3(0, resource_shared);
    // worker2 and worker3 share *shared_resource


    Worker worker4(0, std::move(resource));  //resource is empty
}

Live Demo