如果托管对象的所有权没有被转移(并且因为它是unique_ptr,所有权不能被共享),那么将被调用函数中的逻辑与所有权的概念分开会更正确。我们通过引用调用来做到这一点。
这是一种复杂的说法:
给定:
std::unique_ptr<Thing> thing_ptr;
改变事物:
// declaration
void doSomethingWith(Thing& thing);
// called like this
doSomethingWith(*thing_ptr);
在不修改的情况下使用事物。
// declaration
void doSomethingWith(const Thing& thing);
// called like this
doSomethingWith(*thing_ptr);
您唯一想在函数签名中提及unique_ptr 的情况是您要转让所有权:
// declaration
void takeMyThing(std::unique_ptr<Thing> p);
// call site
takeMyThing(std::move(thing_ptr));
你永远不需要这样做:
void useMyThing(const std::unique_ptr<Thing>& p);
这是一个坏主意的原因是,如果混淆了useMyThing的逻辑和所有权的概念,从而缩小了重用的范围。
考虑:
useMyThing(const Thing& thing);
Thing x;
std::unique_ptr<Thing> thing_ptr = makeAThing();
useMyThing(x);
useMyThing(*thing_ptr);
更新:
注意问题的更新 - 存储(非拥有)对该对象的引用。
做到这一点的一种方法确实是存储一个指针。但是,指针可能会出现逻辑错误,因为它们可以合法地为空。指针的另一个问题是它们不能很好地与std:: algorithms 和容器一起使用——需要自定义比较函数等。
有一种std::-compliant 方法可以做到这一点 - std::reference_wrapper<>
所以不是这样:
std::vector<Thing*> my_thing_ptrs;
这样做:
std::vector<std::reference_wrapper<Thing>> my_thing_refs;
由于std::reference_wrapper<T> 定义了一个运算符T&,您可以在任何期望T 的表达式中使用reference_wrapped 对象。
例如:
std::unique_ptr<Thing> t1 = make_thing();
std::unique_ptr<Thing> t2 = make_thing();
std::unique_ptr<Thing> t3 = make_thing();
std::vector<std::reference_wrapper<const Thing>> thing_cache;
store_thing(*t1);
store_thing(*t2);
store_thing(*t3);
int total = 0;
for(const auto& t : thing_cache) {
total += value_of_thing(t);
}
地点:
void store_thing(const Thing& t) {
thing_cache.push_back(std::cref(t));
}
int value_of_thing(const Thing& t) {
return <some calculation on t>;
}