C++ 如何传递 std::unique_ptr ?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11277249/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to pass std::unique_ptr around?
提问by lvella
I am having my first attempt at using C++11 unique_ptr
; I am replacing a polymorphic raw pointer inside a project of mine, which is owned by one class, but passed around quite frequently.
我第一次尝试使用 C++11 unique_ptr
;我正在替换我的一个项目中的多态原始指针,该项目归一个类所有,但经常传递。
I used to have functions like:
我曾经有过这样的功能:
bool func(BaseClass* ptr, int other_arg) {
bool val;
// plain ordinary function that does something...
return val;
}
But I soon realized that I wouldn't be able to switch to:
但我很快意识到我无法切换到:
bool func(std::unique_ptr<BaseClass> ptr, int other_arg);
Because the caller would have to handle the pointer ownership to the function, what I don't want to. So, what is the best solution to my problem?
因为调用者必须处理函数的指针所有权,这是我不想做的。那么,我的问题的最佳解决方案是什么?
I though of passing the pointer as reference, like this:
我虽然将指针作为参考传递,如下所示:
bool func(const std::unique_ptr<BaseClass>& ptr, int other_arg);
But I feel very uncomfortable in doing so, firstly because it seems non instinctive to pass something already typed as _ptr
as reference, what would be a reference of a reference. Secondly because the function signature gets even bigger. Thirdly, because in the generated code, it would be necessary two consecutive pointer indirections to reach my variable.
但是这样做我感到很不舒服,首先是因为传递已经输入_ptr
为引用的东西似乎不是本能的,什么是引用的引用。其次,因为函数签名变得更大。第三,因为在生成的代码中,需要两个连续的指针间接访问才能到达我的变量。
回答by R. Martinho Fernandes
If you want the function to use the pointee, pass a reference to it. There's no reason to tie the function to work only with some kind of smart pointer:
如果您希望函数使用指针对象,请传递对它的引用。没有理由将函数与某种智能指针绑定在一起:
bool func(BaseClass& base, int other_arg);
And at the call site use operator*
:
并在呼叫站点使用operator*
:
func(*some_unique_ptr, 42);
Alternatively, if the base
argument is allowed to be null, keep the signature as is, and use the get()
member function:
或者,如果base
允许参数为空,则保持签名不变,并使用get()
成员函数:
bool func(BaseClass* base, int other_arg);
func(some_unique_ptr.get(), 42);
回答by Victor Savu
The advantage of using std::unique_ptr<T>
(aside from not having to remember to call delete
or delete[]
explicitly) is that it guarantees that a pointer is either nullptr
or it points to a valid instance of the (base) object. I will come back to this after I answer your question, but the first message is DOuse smart pointers to manage the lifetime of dynamically allocated objects.
using 的优点std::unique_ptr<T>
(除了不必记住调用delete
或delete[]
显式)是它保证一个指针要么指向要么nullptr
指向(基)对象的有效实例。在回答您的问题后,我会回到这个话题,但第一条消息是请务必使用智能指针来管理动态分配对象的生命周期。
Now, your problemis actually how to use this with your old code.
现在,您的问题实际上是如何在旧代码中使用它。
My suggestion is that if you don't want to transfer or share ownership, you should alwayspass references to the object. Declare your function like this (with or without const
qualifiers, as needed):
我的建议是,如果您不想转让或共享所有权,则应始终传递对对象的引用。像这样声明你的函数(const
根据需要带或不带限定符):
bool func(BaseClass& ref, int other_arg) { ... }
Then the caller, which has a std::shared_ptr<BaseClass> ptr
will either handle the nullptr
case or it will ask bool func(...)
to compute the result:
然后调用者,它std::shared_ptr<BaseClass> ptr
要么处理这个nullptr
案例,要么要求bool func(...)
计算结果:
if (ptr) {
result = func(*ptr, some_int);
} else {
/* the object was, for some reason, either not created or destroyed */
}
This means that any caller has to promisethat the reference is valid and that it will continue to be valid throughout the execution of the function body.
这意味着任何调用者都必须保证引用是有效的,并且在函数体的整个执行过程中它将继续有效。
Here is the reason why I strongly believe you should notpass raw pointers or references to smart pointers.
这是为什么我坚信你应该之所以没有通过原始指针或引用智能指针。
A raw pointer is only a memory address. Can have one of (at least) 4 meanings:
原始指针只是一个内存地址。可以具有(至少)4种含义之一:
- The address of a block of memory where your desired object is located. (the good)
- The address 0x0 which you can be certain is not dereferencable and might have the semantics of "nothing" or "no object". (the bad)
- The address of a block of memory which is outside of the addressable space of your process (dereferencing it will hopefully cause your program to crash). (the ugly)
- The address of a block of memory which can be dereferenced but which doesn't contain what you expect. Maybe the pointer was accidentally modified and now it points to another writable address (of a completely other variable within your process). Writing to this memory location will cause lots of fun to happen, at times, during the execution, because the OS will not complain as long as you are allowed to write there. (Zoinks!)
- 所需对象所在的内存块的地址。(好)
- 您可以确定的地址 0x0 是不可解引用的,并且可能具有“无”或“无对象”的语义。(坏的)
- 超出进程可寻址空间的内存块的地址(取消引用它可能会导致程序崩溃)。(丑)
- 可以取消引用但不包含您期望的内存块的地址。也许指针被意外修改,现在它指向另一个可写地址(您进程中完全不同的变量)。有时,在执行过程中写入此内存位置会带来很多乐趣,因为只要允许您在那里写入,操作系统就不会抱怨。(放大!)
Correctly using smart pointers alleviates the rather scary cases 3 and 4, which are usually not detectable at compile time and which you generally only experience at runtime when your program crashes or does unexpected things.
正确使用智能指针可以缓解相当可怕的情况 3 和 4,它们通常在编译时无法检测到,并且通常只有在程序崩溃或出现意外情况时才会在运行时遇到。
Passing smart pointers as arguments has two disadvantages: you cannot change the const
-ness of the pointedobject without making a copy (which adds overhead for shared_ptr
and is not possible for unique_ptr
), and you are still left with the second (nullptr
) meaning.
将智能指针作为参数传递有两个缺点:您不能在不复制的情况下更改指向对象的const
-ness (这会增加 的开销并且不可能用于),并且您仍然保留第二个 ( ) 含义。shared_ptr
unique_ptr
nullptr
I marked the second case as (the bad) from a design perspective. This is a more subtle argument about responsibility.
我从设计的角度将第二种情况标记为(坏的)。这是一个关于责任的更微妙的论点。
Imagine what it means when a function receives a nullptr
as its parameter. It first has to decide what to do with it: use a "magical" value in place of the missing object? change behavior completely and compute something else (which doesn't require the object)? panic and throw an exception? Moreover, what happens when the function takes 2, or 3 or even more arguments by raw pointer? It has to check each of them and adapt its behavior accordingly. This adds a whole new level on top of input validation for no real reason.
想象一下,当一个函数接收 anullptr
作为其参数时,这意味着什么。它首先必须决定如何处理它:使用“神奇”值代替丢失的对象?完全改变行为并计算其他东西(不需要对象)?恐慌并抛出异常?此外,当函数通过原始指针接受 2、3 甚至更多参数时会发生什么?它必须检查它们中的每一个并相应地调整其行为。这在没有真正原因的情况下在输入验证之上增加了一个全新的级别。
The caller should be the one with enough contextual information to make these decisions, or, in other words, the badis less frightening the more you know. The function, on the other hand, should just take the caller's promise that the memory it is pointed to is safe to work with as intended. (References are still memory addresses, but conceptually represent a promise of validity.)
呼叫者应该拥有足够的上下文信息来做出这些决定,或者换句话说,你知道的越多,坏事就越不可怕。另一方面,该函数应该只接受调用者的承诺,即它所指向的内存可以安全地按预期工作。(引用仍然是内存地址,但在概念上代表了有效性的承诺。)
回答by Mikael Persson
I agree with Martinho, but I think it is important to point out the ownership semantics of a pass-by-reference. I think the correct solution is to use a simple pass-by-reference here:
我同意 Martinho,但我认为指出传递引用的所有权语义很重要。我认为正确的解决方案是在这里使用一个简单的传递引用:
bool func(BaseClass& base, int other_arg);
The commonly accepted meaning of a pass-by-reference in C++ is like as if the caller of the function tells the function "here, you can borrow this object, use it, and modify it (if not const), but only for the duration of the function body." This is, in no way, in conflict with the ownership rules of the unique_ptr
because the object is merely being borrowed for a short period of time, there is no actual ownership transfer happening (if you lend your car to someone, do you sign the title over to him?).
C++ 中普遍接受的引用传递的含义就像函数的调用者告诉函数“在这里,你可以借用这个对象,使用它,并修改它(如果不是 const),但仅限于函数体的持续时间。” 这绝不是与所有权规则相冲突,unique_ptr
因为该对象只是短期借用,并没有发生实际的所有权转让(如果您将汽车借给某人,您是否在所有权上签名?交给他?)。
So, even though it might seem bad (design-wise, coding practices, etc.) to pull the reference (or even the raw pointer) out of the unique_ptr
, it actually is not because it is perfectly in accordance with the ownership rules set by the unique_ptr
. And then, of course, there are other nice advantages, like clean syntax, no restriction to only objects owned by a unique_ptr
, and so.
因此,即使将引用(甚至原始指针)从 中拉出似乎很糟糕(设计方面、编码实践等)unique_ptr
,但实际上并不是因为它完全符合由的unique_ptr
。然后,当然,还有其他很好的优势,比如干净的语法,不限制只拥有 a 的对象unique_ptr
,等等。
回答by Guilherme Ferreira
Personally, I avoid pulling a reference from a pointer/smart pointer. Because what happens if the pointer is nullptr
? If you change the signature to this:
就个人而言,我避免从指针/智能指针中提取引用。因为如果指针是 会发生什么nullptr
?如果您将签名更改为:
bool func(BaseClass& base, int other_arg);
You might have to protect your code from null pointer dereferences:
您可能必须保护您的代码免受空指针取消引用:
if (the_unique_ptr)
func(*the_unique_ptr, 10);
If the class is the sole owner of the pointer, the second of Martinho's alternative seems more reasonable:
如果该类是指针的唯一所有者,那么 Martinho 的第二种选择似乎更合理:
func(the_unique_ptr.get(), 10);
Alternatively, you can use std::shared_ptr
. However, if there's one single entity responsible for delete
, the std::shared_ptr
overhead does not pay off.
或者,您可以使用std::shared_ptr
. 但是,如果只有一个实体负责delete
,则std::shared_ptr
开销不会得到回报。