std::unique_ptr::get
传递原始指针,还是应该完全不使用std::unique_ptr
而使用和传递std::shared_ptr
?我对有一些偏好,因为该指针的所有者实际上负责清除。 如果我使用共享指针,则可能由于共享指针而使对象保持活动,即使它实际上应该被销毁。
编辑:不幸的是,我忘记提到指针不仅仅是函数调用的参数,而且将存储在其他对象中以建立对象网络结构。 我不喜欢使用共享指针,因为这样就不再清楚谁实际拥有对象了。
std::unique_ptr::get
传递原始指针,还是应该完全不使用std::unique_ptr
而使用和传递std::shared_ptr
?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(const Thing& thing);
Thing x;
std::unique_ptr<Thing> thing_ptr = makeAThing();
useMyThing(x);
useMyThing(*thing_ptr);
更新:
注意到问题的更新 - 存储(非拥有)对此对象的引用。
一种方法是确实存储指针。然而,指针可能存在逻辑错误的可能性,因为它们可以合法地为空。指针的另一个问题是它们与std::算法
和容器不兼容 - 需要自定义比较函数等。
有一种符合std::
标准的方法来做到这一点 - 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>;
}
unique_ptr
被销毁后,代码的其他部分不会再使用它。如果你不能确保这一点,那么 unique_ptr
就不合适。 - Chris Drewvoid func(const Foo& foo);
std::unique_ptr<Foo> ptr;
// allocate ptr...
if(ptr)
func(*ptr);
使用裸指针传递参数:
void func(const Foo* foo);
std::unique_ptr<Foo> ptr;
// allocate ptr...
func(ptr.get());
unique_ptr
被销毁后不再使用指针或引用。如果您无法保证,那么您必须使用shared_ptr
而不是unique_ptr
。观察者可以持有一个weak_ptr
来表示他们没有所有权。unique_ptr
被销毁后不会再使用它。不要过于关注调用者的要求。函数需要传入什么?如果它只在调用期间使用对象,那么使用引用。
void func(const Foo& foo);
shared_ptr
。void func(std::shared_ptr<Foo> foo);
使用Richard Hodges建议的std::reference_wrapper<>
,只要您不需要NULL值选项。
请注意,您的用例似乎需要一个默认构造函数,除非您有一些公共静态实例可用作默认值。
使用一些自定义的not_your_pointer<T>
类型,具有所需的语义:可以默认构造、可复制和可赋值、可以从unique_ptr
转换而来并且是非拥有型的。但是,编写新的智能指针类需要一些思考,因此只有在频繁使用时才值得这样做。
如果您需要优雅地处理悬空引用,请使用std::weak_ptr
并将所有权转移到shared_ptr
(也就是说,只存在一个shared_ptr:不存在所有权的歧义,对象的生命周期也不受影响)
indicate that ownership is not transferred by passing a reference:
void foo(T &obj); // no-one expects this to transfer ownership
called as:
foo(*ptr);
you do lose the ability to pass nullptr
though, if that matters.
indicate that ownership is not transferred by explicitly prohibiting it:
void foo(std::unique_ptr<T> const &p) {
// can't move or reset p to take ownership
// still get non-const access to T
}
this is clear, but does expose your memory management policy to the callee.
pass a raw pointer and document that ownership should not be transferred. This is the weakest and most error prone. Don't do it.
const unique_ptr&
相对于裸指针有什么优势?正如你所说,缺点是它会暴露你的内存管理策略。 - Chris Drewconst_cast
或其他一些技巧来获取它。与原始引用不同,您仍然可以传递 nullptr
。 - Uselessconst unique_ptr&
规范中没有阻止通过 p.get()
获取底层原始指针,并对其进行任何想要的操作(将其存储在数据结构中是 OP 的意图)。而传递原始指针或引用的默认假设是不转移所有权(考虑所有那些使用 const char*
的函数),因此选项 2 真的没有为您带来太多好处。最重要的是,在存储指针的地方非常清楚地记录指针不是所有者。 - Marc van Leeuwenunique_ptr
。 - Chris Drew这个问题已经被 Herb Sutter 在 GotW#91 - 智能指针参数 中讨论过了。它也涉及到性能方面,而其他答案虽然很好,但是更侧重于内存管理。
上面有人建议将智能指针作为const引用传递 - 但后来改变了主意。我们有一些代码滥用了这种方式——它使签名更加“智能”——即更加复杂而没有好处。最糟糕的是当一个初级开发者接手时——他不会像@Michael那样质疑,并从一个坏的例子中“学习”如何做。它可以工作,但是......
对我来说,智能指针参数传递问题足够严重,应该在解释什么是智能指针之后立即在任何书籍/文章/帖子中突出提到。
现在想知道像lint程序是否应该将通过const引用传递标记为风格要素。
unique_ptr
的引用只有在您想要重新设置unique_ptr
时才有用。如果您只想观察指针,则原始指针或引用就可以了。对于unique_ptr
的引用并不更安全。请参见http://herbsutter.com/2013/06/05/gotw-91-solution-smart-pointer-parameters/。 - Chris Drew