C++ 何时使用 shared_ptr 何时使用原始指针?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7657718/
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
When to use shared_ptr and when to use raw pointers?
提问by TripShock
class B;
class A
{
public:
A ()
: m_b(new B())
{
}
shared_ptr<B> GimmeB ()
{
return m_b;
}
private:
shared_ptr<B> m_b;
};
Let's say B is a class that semantically should not exist outside of the lifetime of A, i.e., it makes absolutely no sense for B to exist by itself. Should GimmeB
return a shared_ptr<B>
or a B*
?
假设 B 是一个在语义上不应该存在于 A 生命周期之外的类,即,B 单独存在是完全没有意义的。应该GimmeB
返回 ashared_ptr<B>
还是 a B*
?
In general, is it good practice to completely avoid using raw pointers in C++ code, in lieu of smart pointers?
一般来说,完全避免在 C++ 代码中使用原始指针代替智能指针是一种好习惯吗?
I am of the opinion that shared_ptr
should only be used when there is explicit transfer or sharing of ownership, which I think is quite rare outside of cases where a function allocates some memory, populates it with some data, and returns it, and there is understanding between the caller and the callee that the former is now "responsible" for that data.
我认为shared_ptr
应该只在有明确的所有权转让或共享时使用,我认为除了函数分配一些内存,用一些数据填充它并返回它的情况之外,这是非常罕见的,并且有理解在调用者和被调用者之间,前者现在对该数据“负责”。
采纳答案by Alexandre C.
Your analysis is quite correct, I think. In this situation, I also would return a bare B*
, or even a [const] B&
if the object is guaranteed to never be null.
你的分析非常正确,我认为。在这种情况下,如果保证对象永远不会为空,我也会返回一个空的B*
,甚至是一个[const] B&
。
Having had some time to peruse smart pointers, I arrived at some guidelines which tell me what to do in many cases:
有时间仔细研究智能指针后,我得出了一些指导方针,这些指导方针告诉我在许多情况下该怎么做:
- If you return an object whose lifetime is to be managed by the caller, return
std::unique_ptr
. The caller can assign it to astd::shared_ptr
if it wants. - Returning
std::shared_ptr
is actually quite rare, and when it makes sense, it is generally obvious: you indicate to the caller that it will prolong the lifetime of the pointed-to object beyond the lifetime of the object which was originally maintaining the resource. Returning shared pointers from factories is no exception: you must do this eg. when you usestd::enable_shared_from_this
. - You very rarely need
std::weak_ptr
, except when you want to make sense of thelock
method. This has some uses, but they are rare. In your example, if the lifetime of theA
object was not deterministic from the caller's point of view, this would have been something to consider. - If you return a reference to an existing object whose lifetime the caller cannot control, then return a bare pointer or a reference. By doing so, you tell the caller that an object exists and that she doesn't have to take care of its lifetime. You should return a reference if you don't make use of the
nullptr
value.
- 如果返回其生命周期由调用者管理的对象,则返回
std::unique_ptr
。如果需要,调用者可以将其分配给 astd::shared_ptr
。 - 返回
std::shared_ptr
实际上非常罕见,当它有意义时,通常很明显:您向调用者指示它将延长指向对象的生命周期,超出最初维护资源的对象的生命周期。从工厂返回共享指针也不例外:你必须这样做,例如。当您使用std::enable_shared_from_this
. - 您很少需要
std::weak_ptr
,除非您想了解该lock
方法。这有一些用途,但它们很少见。在您的示例中,如果A
从调用者的角度来看对象的生命周期不是确定性的,那么这将是需要考虑的事情。 - 如果您返回对调用者无法控制其生命周期的现有对象的引用,则返回一个裸指针或引用。通过这样做,你告诉调用者一个对象存在并且她不必关心它的生命周期。如果您不使用该
nullptr
值,您应该返回一个引用。
回答by Mankarse
The question "when should I use shared_ptr
and when should I use raw pointers?" has a very simple answer:
问题“什么时候应该使用shared_ptr
,什么时候应该使用原始指针?” 有一个非常简单的答案:
- Use raw pointers when you do not want to have any ownership attached to the pointer. This job can also often be done with references. Raw pointers can also be used in some low level code (such as for implementing smart pointers, or implementing containers).
- Use
unique_ptr
orscope_ptr
when you want unique ownership of the object. This is the most useful option, and should be used in most cases. Unique ownership can also be expressed by simply creating an object directly, rather than using a pointer (this is even better than using aunique_ptr
, if it can be done). - Use
shared_ptr
orintrusive_ptr
when you want shared ownership of the pointer. This can be confusing and inefficient, and is often not a good option. Shared ownership can be useful in some complex designs, but should be avoided in general, because it leads to code which is hard to understand.
- 当您不想将任何所有权附加到指针时,请使用原始指针。这项工作通常也可以通过参考来完成。原始指针也可用于一些低级代码(例如用于实现智能指针或实现容器)。
- 当您想要对象的唯一所有权时使用
unique_ptr
或scope_ptr
。这是最有用的选项,应该在大多数情况下使用。唯一所有权也可以通过简单地直接创建对象来表达,而不是使用指针(unique_ptr
如果可以的话,这甚至比使用 a 更好)。 - 当您想要共享指针所有权时使用
shared_ptr
或intrusive_ptr
。这可能会令人困惑且效率低下,而且通常不是一个好的选择。共享所有权在一些复杂的设计中很有用,但通常应该避免,因为它会导致代码难以理解。
shared_ptr
s perform a totally different task from raw pointers, and neither shared_ptr
s nor raw pointers are the best option for the majority of code.
shared_ptr
s 执行与原始指针完全不同的任务,shared_ptr
对于大多数代码来说,s 和原始指针都不是最佳选择。
回答by Maxim Egorushkin
The following is a good rule of thumb:
以下是一个很好的经验法则:
- When there is no transfer of or shared ownership references or plain pointers are good enough. (Plain pointers are more flexible than references.)
- When there is transfer of ownership but no shared ownership then
std::unique_ptr<>
is a good choice. Often the case with factory functions. - When there is shared ownership, then it is a good use case for
std::shared_ptr<>
orboost::intrusive_ptr<>
.
- 当没有转移或共享所有权时,引用或普通指针就足够了。(普通指针比引用更灵活。)
- 当有所有权转让但没有共享所有权时,这
std::unique_ptr<>
是一个不错的选择。通常是工厂函数的情况。 - 当存在共享所有权时,则是
std::shared_ptr<>
or 的一个很好的用例boost::intrusive_ptr<>
。
It is best to avoid shared ownership, partly because they are most expensive in terms of copying and std::shared_ptr<>
takes double of the storage of a plain pointer, but, most importantly, because they are conducive for poor designs where there are no clear owners, which, in turn, leads to a hairball of objects that cannot destroy because they hold shared pointers to each other.
最好避免共享所有权,部分原因是它们在复制方面成本最高,并且std::shared_ptr<>
占用普通指针的两倍存储空间,但最重要的是,因为它们有利于没有明确所有者的糟糕设计,其中,反过来,导致无法销毁的对象的毛球,因为它们持有彼此共享的指针。
The best design is where clear ownership is established and is hierarchical, so that, ideally, no smart pointers are required at all. For example, if there is a factory that creates unique objects or returns existing ones, it makes sense for the factory to own the objects it creates and just keep them by value in an associative container (such as std::unordered_map
), so that it can return plain pointers or references to its users. This factory must have lifetime that starts before its first user and ends after its last user (the hierarchical property), so that users cannot possible have a pointer to an already destroyed object.
最好的设计是建立明确的所有权并且是分层的,因此理想情况下根本不需要智能指针。例如,如果有一个创建唯一对象或返回现有对象的工厂,那么工厂拥有它创建的对象并将它们按值保存在关联容器(例如std::unordered_map
)中是有意义的,以便它可以返回普通对象指向其用户的指针或引用。这个工厂的生命周期必须在它的第一个用户之前开始并在它的最后一个用户之后结束(层次属性),这样用户就不可能有一个指向已经销毁的对象的指针。
回答by reko_t
If you don't want the callee of GimmeB() to be able to extend the lifetime of the pointer by keeping a copy of the ptr after the instance of A dies, then you definitely should not return a shared_ptr.
如果您不希望 GimmeB() 的被调用者能够通过在 A 的实例死亡后保留 ptr 的副本来延长指针的生命周期,那么您绝对不应该返回 shared_ptr。
If the callee is not supposed to keep the returned pointer for long periods of time, i.e. there's no risk of the instance of A's lifetime expiring before the pointer's, then raw pointer would be better. But even a better choice is simply to use a reference, unless there's a good reason to use an actual raw pointer.
如果被调用者不应该长时间保留返回的指针,即没有 A 的生命周期实例在指针生命周期之前到期的风险,那么原始指针会更好。但更好的选择是简单地使用引用,除非有充分的理由使用实际的原始指针。
And finally in the case that the returned pointer can exist after the lifetime of the A instance has expired, but you don't want the pointer itself extend the lifetime of the B, then you can return a weak_ptr, which you can use to test whether it still exists.
最后,如果返回的指针可以在 A 实例的生命周期到期后仍然存在,但您不希望指针本身延长 B 的生命周期,那么您可以返回一个weak_ptr,您可以使用它来测试是否还存在。
The bottom line is that there's usually a nicer solution than using a raw pointer.
最重要的是,通常有比使用原始指针更好的解决方案。
回答by Matthieu M.
I agree with your opinion that shared_ptr
is best used when explicit sharing of resources occurs, however there are other types of smart pointers available.
我同意你的意见,shared_ptr
最好在发生显式共享资源时使用,但是还有其他类型的智能指针可用。
In your precise case: why not return a reference ?
在您的确切情况下:为什么不返回引用?
A pointer suggests that the data might be null, however here there will always be a B
in your A
, thus it will never be null. The reference asserts this behavior.
一个指针表明数据可能为空,但是在这里B
你的 中总会有 a A
,因此它永远不会为空。参考断言了这种行为。
That being said, I have seen people advocating the use of shared_ptr
even in non-shared environments, and giving weak_ptr
handles, with the idea of "securing" the application and avoiding stale pointers. Unfortunately, since you can recover a shared_ptr
from the weak_ptr
(and it is the only way to actually manipulate the data), this is still shared ownership even if it was not meant to be.
话虽如此,我已经看到人们提倡shared_ptr
甚至在非共享环境中使用 ,并提供weak_ptr
句柄,以“保护”应用程序并避免陈旧指针的想法。不幸的是,由于您可以shared_ptr
从weak_ptr
(并且它是实际操作数据的唯一方法)中恢复 a ,因此即使不打算这样做,这仍然是共享所有权。
Note: there is a subtle bug with shared_ptr
, a copy of A
will share the same B
as the original by default, unless you explicitly write a copy constructor and a copy assignment operator. And of course you would not use a raw pointer in A
to hold a B
, would you :) ?
注意: 有一个微妙的错误shared_ptr
,默认情况下, 的副本A
将B
与原始副本共享相同的内容,除非您明确编写复制构造函数和复制赋值运算符。当然,您不会使用原始指针A
来保存 a B
,您会吗:) ?
Of course, another question is whether you actually need to do so. One of the tenets of good design is encapsulation. To achieve encapsulation:
当然,另一个问题是您是否真的需要这样做。良好设计的原则之一是封装。实现封装:
You shall not return handles to your internals (see Law of Demeter).
您不得将句柄返回到您的内部(参见迪米特法则)。
so perhaps the real answer to your question is that instead of giving away a reference or pointer to B
, it should only be modified through A
's interface.
所以也许对您的问题的真正答案是,不应提供对 的引用或指针B
,而应仅通过A
的接口进行修改。
回答by Ricibob
- You allocate B at constuction of A.
- You say B shouldn't persist outside As lifetime.
Both these point to B being a member of A and a just returning a reference accessor. Are you overengineering this?
- 您在构造 A 时分配 B。
- 你说 B 不应该在 As 生命周期之外坚持。
这两个都指向 B 是 A 的成员,并且只是返回一个引用访问器。你在过度设计这个吗?
回答by thiton
Generally, I would avoid using raw pointers as far as possible since they have very ambiguous meaning - you might have to deallocate the pointee, but maybe not, and only human-read and -written documentation tells you what the case is. And documentation is always bad, outdated or misunderstood.
通常,我会尽可能避免使用原始指针,因为它们的含义非常模糊 - 您可能必须取消分配指针对象,但也可能不需要,并且只有人工阅读和编写的文档会告诉您情况是什么。文档总是糟糕的、过时的或被误解的。
If ownership is an issue, use a smart pointer. If not, I'd use a reference if practicable.
如果所有权是个问题,请使用智能指针。如果没有,如果可行,我会使用参考。
回答by camino
I found that the C++ Core Guidelines give some very useful hints for this question:
我发现 C++ Core Guidelines 为这个问题提供了一些非常有用的提示:
To use raw pointer(T*) or smarter pointer depends on who owns the object (whose responsibility to release memory of the obj).
使用原始指针(T*)或更智能的指针取决于谁拥有对象(谁有责任释放 obj 的内存)。
own :
smart pointer, owner<T*>
not own:
T*, T&, span<>
自己的 :
smart pointer, owner<T*>
不拥有:
T*, T&, span<>
owner<>, span<> is defined in Microsoft GSL library
owner<>, span<> 在 Microsoft GSL 库中定义
here is the rules of thumb:
这是经验法则:
1) never use raw pointer(or not own types) to pass ownership
1)永远不要使用原始指针(或不是自己的类型)来传递所有权
2) smart pointer should only be used when ownership semantics are intended
2) 智能指针只应在需要所有权语义时使用
3) T* or owner designate a individual object(only)
3) T* 或所有者指定单个对象(仅限)
4) use vector/array/span for array
4) 使用向量/数组/跨度作为数组
5) To my undetstanding, shared_ptr is usually used when you don't know who will release the obj, for example, one obj is used by multi-thread
5)我不明白,shared_ptr通常在你不知道谁会释放obj的时候使用,例如一个obj被多线程使用
回答by hamstergene
It is good practice to avoid using raw pointers, but you can not just replace everything with shared_ptr
. In the example, users of your class will assume that it's ok to extend B's lifetime beyond that of A's, and may decide to hold the returned B object for some time for their own reasons. You should return a weak_ptr
, or, if B absolutely cannot exist when A is destroyed, a reference to B or simply a raw pointer.
避免使用原始指针是一种很好的做法,但您不能将所有内容都替换为shared_ptr
. 在这个例子中,你的类的用户会认为可以将 B 的生命周期延长到 A 的生命周期之外,并且可能出于自己的原因决定将返回的 B 对象保留一段时间。您应该返回 a weak_ptr
,或者,如果 B 在 A 被销毁时绝对不存在,则返回对 B 的引用或简单的原始指针。
回答by seand
When you say: "Let's say B is a class that semantically should not exist outside of the lifetime of A"
当你说:“假设 B 是一个在 A 的生命周期之外在语义上不应该存在的类”
This tells me B should logicallynot exist without A, but what about physically existing? If you can be sure no one will try using a *B after A dtors than perhaps a raw pointer will be fine. Otherwise a smarter pointer may be appropriate.
这告诉我 B 在逻辑上不应该在没有 A 的情况下存在,但是物理上的存在呢?如果您可以确定没有人会在 A dtors 之后尝试使用 *B ,那么原始指针可能会很好。否则,更智能的指针可能是合适的。
When clients have a direct pointer to A you have to trust they'll handle it appropriately; not try dtoring it etc.
当客户有一个指向 A 的直接指针时,你必须相信他们会适当地处理它;不要尝试 dtoring 它等。