在对象之间拥有或共享资源
Owning or sharing a resource between objects
A 具有以下类型的工作对象集合:
template<class T> struct Worker;
不同的工人,有不同的模板参数T
,使用一些资源
struct Resource;
不同的 Worker
使用的 Resource
都具有相同的类型,但可能具有不同的值。资源一般都比较重,应该避免不必要的拷贝。资源仅由工作人员使用,有些是独占使用的,有些是在他们之间共享的(此信息在编译时可用)。如果一个资源被独占使用,我希望 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_
}
这些方法显然非常不同,但在当前情况下实际上是等效的。我都不喜欢他们两个。
在第一种情况下,我不喜欢将资源存储在 Worker
之外的某个局部变量中。在 Worker
.
中获得悬空引用存在潜在风险
在第二种情况下,在创建所有 Worker
后,不必要的计数引用仍保留在 resource
中。我可以按值获取 shared_ptr
并在最后一个 Worker
构造中使用 std::move
,但这需要我显式地跟踪最后一个构造函数 - 这是获得错误的绝佳方法。
这些问题不会影响代码的正确性,但可能意味着整个设计存在缺陷。我认为应该有更好的方法。请解释在这种情况下的最佳做法是什么。 (我知道我的问题可能太笼统了,我需要的解决方案可能取决于我创建资源和工作人员的确切方式;好的解决方案可能来自对问题的不同看法,目前我陷入了两种方法我在上面概述了。)
In the second case unnecessary counted reference remains in resource
after all Workers have been created. I could take shared_ptr
by value
and use std::move
in the last Worker construction, but it would
require me to track the last constructor explicitly - an excellent way
to get a bug.
不,你没有;默认情况下使用 unique_ptr
创建资源,需要时将其转换为 shared_ptr
需要花费 "nothing"。
考虑只使用接受 std::shared_ptr
by value 的构造函数;
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
}
A 具有以下类型的工作对象集合:
template<class T> struct Worker;
不同的工人,有不同的模板参数T
,使用一些资源
struct Resource;
不同的 Worker
使用的 Resource
都具有相同的类型,但可能具有不同的值。资源一般都比较重,应该避免不必要的拷贝。资源仅由工作人员使用,有些是独占使用的,有些是在他们之间共享的(此信息在编译时可用)。如果一个资源被独占使用,我希望 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_ }
这些方法显然非常不同,但在当前情况下实际上是等效的。我都不喜欢他们两个。
在第一种情况下,我不喜欢将资源存储在 Worker
之外的某个局部变量中。在 Worker
.
在第二种情况下,在创建所有 Worker
后,不必要的计数引用仍保留在 resource
中。我可以按值获取 shared_ptr
并在最后一个 Worker
构造中使用 std::move
,但这需要我显式地跟踪最后一个构造函数 - 这是获得错误的绝佳方法。
这些问题不会影响代码的正确性,但可能意味着整个设计存在缺陷。我认为应该有更好的方法。请解释在这种情况下的最佳做法是什么。 (我知道我的问题可能太笼统了,我需要的解决方案可能取决于我创建资源和工作人员的确切方式;好的解决方案可能来自对问题的不同看法,目前我陷入了两种方法我在上面概述了。)
In the second case unnecessary counted reference remains in resource after all Workers have been created. I could take
shared_ptr
by value and usestd::move
in the last Worker construction, but it would require me to track the last constructor explicitly - an excellent way to get a bug.
不,你没有;默认情况下使用 unique_ptr
创建资源,需要时将其转换为 shared_ptr
需要花费 "nothing"。
考虑只使用接受 std::shared_ptr
by value 的构造函数;
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
}