C++ 我什么时候使用哪种指针?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8706192/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 19:01:19  来源:igfitidea点击:

Which kind of pointer do I use when?

c++pointersc++11smart-pointersc++-faq

提问by sbi

Ok, so the last time I wrote C++ for a living, std::auto_ptrwas all the std lib had available, and boost::shared_ptrwas all the rage. I never really looked into the other smart pointer types boost provided. I understand that C++11 now provides some of the types boost came up with, but not all of them.

好吧,所以我最后一次以 C++ 为生时,std::auto_ptr所有的标准库都可用,并且boost::shared_ptr风靡一时。我从未真正研究过 boost 提供的其他智能指针类型。我知道 C++11 现在提供了 boost 提出的一些类型,但不是全部。

So does someone have a simple algorithm to determine when to use which smart pointer? Preferably including advice regarding dumb pointers (raw pointers like T*) and the rest of the boost smart pointers. (Something like thiswould be great).

那么有人有一个简单的算法来确定何时使用哪个智能指针吗?最好包括关于哑指针(原始指针,如T*)和其余 boost 智能指针的建议。(喜欢的东西将是巨大的)。

采纳答案by Xeo

Shared ownership:
The shared_ptrand weak_ptrthe standard adopted are pretty much the same as their Boost counterparts. Use them when you need to share a resource and don't know which one will be the last to be alive. Use weak_ptrto observe the shared resource without influencing its lifetime, not to break cycles. Cycles with shared_ptrshouldn't normally happen - two resources can't own each other.

共享所有权:
采用的标准shared_ptrBoostweak_ptr标准几乎相同。当您需要共享资源并且不知道哪一个将是最后一个还活着时,请使用它们。用于在不影响其生命周期的情况下观察共享资源,而不是打破循环。通常不应该发生循环- 两个资源不能相互拥有。weak_ptrshared_ptr

Note that Boost additionally offers shared_array, which might be a suitable alternative to shared_ptr<std::vector<T> const>.

请注意,Boost 还提供shared_array,这可能是shared_ptr<std::vector<T> const>.

Next, Boost offers intrusive_ptr, which are a lightweight solution if your resource offers reference-counted management already and you want to adopt it to the RAII principle. This one was not adopted by the standard.

接下来,Boost 提供intrusive_ptr,这是一个轻量级解决方案,如果您的资源已经提供引用计数管理,并且您希望将其应用于 RAII 原则。这个标准没有采用。

Unique ownership:
Boost also has a scoped_ptr, which is not copyable and for which you can not specify a deleter. std::unique_ptris boost::scoped_ptron steroids and should be your default choice when you need a smart pointer. It allows you to specify a deleter in its template arguments and is movable, unlike boost::scoped_ptr. It is also fully usable in STL containers as long as you don't use operations that need copyable types (obviously).

唯一所有权:
Boost 也有一个scoped_ptr,它是不可复制的,并且你不能为其指定一个删除器。std::unique_ptrboost::scoped_ptr类固醇,应该是你当你需要一个智能指针默认选择。它允许您在其模板参数中指定删除器并且是可移动的,与boost::scoped_ptr. 只要您不使用需要可复制类型的操作(显然),它在 STL 容器中也完全可用。

Note again, that Boost has an array version: scoped_array, which the standard unified by requiring std::unique_ptr<T[]>partial specialization that will delete[]the pointer instead of deleteing it (with the default_deleter). std::unique_ptr<T[]>also offers operator[]instead of operator*and operator->.

再次注意,Boost 有一个数组版本: scoped_array,标准通过要求std::unique_ptr<T[]>部分特化来统一delete[]指针而不是指针delete(使用default_deleter)。std::unique_ptr<T[]>还提供operator[]代替operator*operator->

Note that std::auto_ptris still in the standard, but it is deprecated. §D.10 [depr.auto.ptr]

请注意,std::auto_ptr它仍在标准中,但已弃用§D.10 [depr.auto.ptr]

The class template auto_ptris deprecated. [ Note:The class template unique_ptr(20.7.1) provides a better solution. —end note]

auto_ptr不推荐使用类模板。[注意:类模板unique_ptr(20.7.1)提供了更好的解决方案。——尾注]

No ownership:
Use dumb pointers (raw pointers) or references for non-owning referencesto resources and when you know that the resource will outlivethe referencing object / scope. Prefer references and use raw pointers when you need either nullability or resettability.

无所有权:
使用哑指针(原始指针)或对资源的非拥有引用的引用,并且当您知道资源将超过引用对象/范围时。当您需要可空性或可重置性时,更喜欢引用并使用原始指针。

If you want a non-owning reference to a resource, but you don't know if the resource will outlive the object that references it, pack the resource in a shared_ptrand use a weak_ptr- you can test if the parent shared_ptris alive with lock, which will return a shared_ptrthat is non-null if the resource still exists. If want to test whether the resource is dead, use expired. The two may sound similar, but are very different in the face of concurrent execution, as expiredonly guarantees its return value for that single statement. A seemingly innocent test like

如果您想要对资源的非拥有引用,但您不知道该资源是否会比引用它的对象寿命更长,请将资源打包在 a 中shared_ptr并使用 a weak_ptr- 您可以使用来测试父项shared_ptr是否还活着lock,这将shared_ptr如果资源仍然存在,则返回非空值。如果要测试资源是否已死,请使用expired. 两者听起来可能很相似,但在并发执行方面却大不相同,因为expired仅保证其对单个语句的返回值。一个看似无辜的测试,如

if(!wptr.expired())
  something_assuming_the_resource_is_still_alive();

is a potential race condition.

是潜在的竞争条件。

回答by Peter Alexander

Deciding what smart pointer to use is a question of ownership. When it comes to resource management, object A ownsobject B if it is in control of the lifetime of object B. For example, member variables are owned by their respective objects because the lifetime of member variables is tied to the lifetime of the object. You choose smart pointers based on how the object is owned.

决定使用什么智能指针是一个所有权问题。在资源管理方面,如果对象 A控制对象 B 的生命周期,则对象 A拥有对象 B。例如,成员变量由其各自的对象拥有,因为成员变量的生命周期与对象的生命周期相关。您可以根据对象的拥有方式选择智能指针。

Note that ownership in a software system is separate from ownership as we would think of it outside of software. For example, a person might "own" their home, but that doesn't necessarily mean that a Personobject has control over the lifetime of a Houseobject. Conflating these real world concepts with software concepts is a sure-fire way to program yourself into a hole.

请注意,软件系统中的所有权与我们在软件之外认为的所有权是分开的。例如,一个人可能“拥有”他们的家,但这并不一定意味着一个Person对象可以控制对象的生命周期House。将这些现实世界的概念与软件概念混为一谈是让自己陷入困境的可靠方法。



If you have sole ownership of the object, use std::unique_ptr<T>.

如果您拥有对象的唯一所有权,请使用std::unique_ptr<T>.

If you have shared ownership of the object...
- If there are no cycles in ownership, use std::shared_ptr<T>.
- If there are cycles, define a "direction" and use std::shared_ptr<T>in one direction and std::weak_ptr<T>in the other.

如果您拥有对象的共享所有权...
- 如果所有权中没有循环,请使用std::shared_ptr<T>.
- 如果有循环,定义一个“方向”并std::shared_ptr<T>在一个方向和另一个方向使用std::weak_ptr<T>

If the object owns you, but there is potential of having no owner, use normal pointers T*(e.g. parent pointers).

如果对象拥有您,但可能没有所有者,请使用普通指针T*(例如父指针)。

If the object owns you (or otherwise has guaranteed existence), use references T&.

如果对象拥有您(或以其他方式保证存在),请使用引用T&



Caveat: Be aware of the costs of smart pointers. In memory or performance limited environments, it could be beneficial to just use normal pointers with a more manual scheme for managing memory.

警告:注意智能指针的成本。在内存或性能受限的环境中,仅使用普通指针和更手动的内存管理方案可能是有益的。

The costs:

费用:

  • If you have a custom deleter (e.g. you use allocation pools) then this will incur overhead per pointer that may be easily avoided by manual deletion.
  • std::shared_ptrhas the overhead of a reference count increment on copy, plus a decrement on destruction followed by a 0-count check with deletion of the held object. Depending on the implementation, this can bloat your code and cause performance issues.
  • Compile time. As with all templates, smart pointers contribute negatively to compile times.
  • 如果您有自定义删除器(例如您使用分配池),那么这将导致每个指针的开销,而手动删除可以轻松避免这些开销。
  • std::shared_ptr具有复制时引用计数递增的开销,加上销毁时递减,然后是 0 计数检查并删除持有的对象。根据实现的不同,这可能会使您的代码膨胀并导致性能问题。
  • 编译时间。与所有模板一样,智能指针对编译时间有负面影响。


Examples:

例子:

struct BinaryTree
{
    Tree* m_parent;
    std::unique_ptr<BinaryTree> m_children[2]; // or use std::array...
};

A binary tree does not own its parent, but the existence of a tree implies the existence of its parent (or nullptrfor root), so that uses a normal pointer. A binary tree (with value semantics) has sole ownership of its children, so those are std::unique_ptr.

二叉树不拥有它的父节点,但一棵树的存在意味着它的父节点(或nullptr根节点)的存在,因此使用普通指针。二叉树(具有值语义)对其子树拥有唯一所有权,因此它们是std::unique_ptr.

struct ListNode
{
    std::shared_ptr<ListNode> m_next;
    std::weak_ptr<ListNode> m_prev;
};

Here, the list node owns its next and previous lists, so we define a direction and use shared_ptrfor next and weak_ptrfor prev to break the cycle.

在这里,列表节点拥有它的下一个和上一个列表,因此我们定义了一个方向并使用shared_ptrfor next 和weak_ptrfor prev 来打破循环。

回答by Puppy

Use unique_ptr<T>all the time except when you need reference counting, in which case use shared_ptr<T>(and for very rare cases, weak_ptr<T>to prevent reference cycles). In almost every case, transferrable unique ownership is just fine.

一直使用unique_ptr<T>,除非您需要引用计数,在这种情况下使用shared_ptr<T>(并且在极少数情况下,weak_ptr<T>以防止引用循环)。几乎在所有情况下,可转让的独特所有权都很好。

Raw pointers: Good only if you need covariant returns, non-owning pointing which can happen. They're not terrifically useful otherwise.

原始指针:仅当您需要协变返回,可能发生的非拥有指向时才好。否则它们并没有多大用处。

Array pointers: unique_ptrhas a specialization for T[]which automatically calls delete[]on the result, so you can safely do unique_ptr<int[]> p(new int[42]);for example. shared_ptryou'd still need a custom deleter, but you wouldn't need a specialized shared or unique array pointer. Of course, such things are usually best replaced by std::vectoranyway. Unfortunately shared_ptrdoes not provide an array access function, so you'd still have to manually call get(), but unique_ptr<T[]>provides operator[]instead of operator*and operator->. In any case, you have to bounds check yourself. This makes shared_ptrslightly less user-friendly, although arguably the generic advantage and no Boost dependency makes unique_ptrand shared_ptrthe winners again.

数组指针:unique_ptr具有T[]自动调用delete[]结果的特化,因此您可以安全地执行unique_ptr<int[]> p(new int[42]);例如。shared_ptr您仍然需要自定义删除器,但不需要专门的共享或唯一数组指针。当然,这样的东西通常最好用std::vector反正代替。不幸的是,shared_ptr它没有提供数组访问功能,因此您仍然必须手动调用get(),而是unique_ptr<T[]>提供operator[]代替operator*operator->。无论如何,你必须自己检查边界。这使得shared_ptr用户友好性略有下降,尽管可以说是通用优势和没有 Boost 依赖使unique_ptr并且shared_ptr再次成为赢家。

Scoped pointers: Made irrelevant by unique_ptr, just like auto_ptr.

作用域指针:与 无关unique_ptr,就像auto_ptr

There's really nothing more to it. In C++03 without move semantics this situation was very complicated, but in C++11 the advice is very simple.

真的没有别的了。在没有移动语义的 C++03 中,这种情况非常复杂,但在 C++11 中,建议非常简单。

There are still uses for other smart pointers, like intrusive_ptror interprocess_ptr. However, they're veryniche and completely unnecessary in the general case.

还有其他智能指针的用途,例如intrusive_ptrinterprocess_ptr。但是,它们非常小众,在一般情况下完全没有必要。

回答by Lalaland

Cases of when to use unique_ptr:

何时使用的情况unique_ptr

  • Factory methods
  • Members that are pointers (pimpl included)
  • Storing pointers in stl containters (to avoid moves)
  • Use of large local dynamic objects
  • 工厂方法
  • 指针成员(包括 pimpl)
  • 将指针存储在 stl 容器中(以避免移动)
  • 使用大型本地动态对象

Cases of when to use shared_ptr:

何时使用的情况shared_ptr

  • Sharing objects across threads
  • Sharing objects in general
  • 跨线程共享对象
  • 一般共享对象

Cases of when to use weak_ptr:

何时使用的情况weak_ptr

  • Large map that acts as a general reference (ex. a map of all open sockets)
  • 用作一般参考的大地图(例如,所有打开的套接字的地图)

Feel free to edit and add more

随意编辑和添加更多