【发布时间】:2018-08-26 10:35:30
【问题描述】:
A 有一组具有以下类型的工作对象:
template<class T> struct Worker;
不同的工人,有不同的模板参数T,使用一些资源
struct Resource;
不同Workers 使用的Resources 都具有相同的类型但可能有不同的值。资源通常很重,应避免不必要的副本。资源仅供工作人员使用,有些是专门使用的,有些是在他们之间共享的(此信息在编译时可用)。如果一个资源被独占使用,我希望 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
我看到了两种直接的方法:
-
使用模板参数来确定
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 } -
使用
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_ }
这些方法显然非常不同,但在当前情况下实际上是等效的。我都不喜欢他们俩。
在第一种情况下,我不喜欢资源存储在Workers 之外的某个局部变量中。在Worker 中获得悬空引用存在潜在风险。
在第二种情况下,在创建了所有 Workers 之后,不必要的计数引用仍保留在 resource 中。我可以按值获取shared_ptr 并在最后一个Worker 构造中使用std::move,但这需要我显式跟踪最后一个构造函数——这是一个很好的解决错误的方法。
这些问题不会影响代码的正确性,但可能意味着整个设计存在缺陷。我认为应该有更好的方法。请解释在这种情况下的最佳做法是什么。 (我知道我的问题可能过于笼统,我需要的解决方案可能取决于我创建资源和工人的确切方式;好的解决方案可能来自对问题的不同看法,目前我陷入了两种方法我在上面概述了。)
【问题讨论】:
-
工人的生命周期是否以任何方式相互关联?它们是否可能由单个对象等管理?我认为,您决定共享资源的方式应该很大程度上取决于此。例如。如果所有工作人员都由一个对象管理(因此他们的生命周期绑定到该对象),您可能应该让该对象也管理他们的资源,并且只传递对资源的引用以供工作人员处理。另一方面,如果您对工人之间的终生关系一无所知,您可能应该选择 std::share_ptr 。
标签: c++ shared-ptr c++17