提问人:sbi 提问时间:1/3/2012 最后编辑:Communitysbi 更新时间:9/16/2018 访问量:40849
何时使用哪种指针?
Which kind of pointer do I use when?
问:
好吧,所以上一次我以写 C++ 为生的时候,所有的标准库都可用,而且风靡一时。我从未真正研究过提供的其他智能指针类型提升。我知道 C++11 现在提供了 boost 提出的一些类型,但不是全部。std::auto_ptr
boost::shared_ptr
那么,是否有人有一个简单的算法来确定何时使用哪个智能指针呢?最好包括有关哑指针(原始指针,如)和其余的boost智能指针的建议。(这样的东西会很棒)。T*
答:
除非需要参考计数,否则请始终使用,在这种情况下,请使用(在极少数情况下,以防止参考循环)。在几乎所有情况下,可转让的唯一所有权都很好。unique_ptr<T>
shared_ptr<T>
weak_ptr<T>
原始指针:仅当您需要协变返回时才好,非拥有指向可能会发生。否则,它们就没有太大的用处。
数组指针:具有自动调用结果的专用化,因此您可以安全地执行例如。 您仍然需要一个自定义删除器,但不需要专门的共享或唯一数组指针。当然,这些东西通常最好用无论如何来代替。不幸的是,它没有提供数组访问功能,因此您仍然需要手动调用 ,但提供而不是 和。无论如何,您都必须对自己进行边界检查。这使得用户友好性略有下降,尽管可以说是通用优势和没有 Boost 依赖性,并且再次成为赢家。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
unique_ptr
shared_ptr
作用域指针:由 使 变得无关紧要,就像 一样。unique_ptr
auto_ptr
真的没什么可说的了。在没有移动语义的 C++03 中,这种情况非常复杂,但在 C++11 中,建议非常简单。
其他智能指针仍有用途,如 或 。但是,它们在一般情况下非常小众,完全没有必要。intrusive_ptr
interprocess_ptr
评论
std::unique_ptr<T[]>
提供而不是 和 。确实,您仍然需要自己进行绑定检查。operator[]
operator*
operator->
决定使用哪个智能指针是一个所有权问题。在资源管理方面,如果对象 A 控制对象 B 的生存期,则对象 A 拥有对象 B。例如,成员变量由其各自的对象拥有,因为成员变量的生存期与对象的生存期相关联。您可以根据对象的拥有方式来选择智能指针。
请注意,软件系统中的所有权与所有权是分开的,因为我们认为它是在软件之外的。例如,一个人可能“拥有”他们的家,但这并不一定意味着对象可以控制对象的生命周期。将这些现实世界的概念与软件概念混为一谈,是将自己编程成洞的可靠方法。Person
House
如果您拥有该对象的唯一所有权,请使用 .std::unique_ptr<T>
如果您共享了对象的所有权...
- 如果所有权没有周期,请使用 .
- 如果有循环,请定义一个“方向”,并在一个方向和另一个方向上使用。std::shared_ptr<T>
std::shared_ptr<T>
std::weak_ptr<T>
如果对象拥有您,但可能没有所有者,请使用普通指针(例如父指针)。T*
如果对象拥有您(或以其他方式保证存在),请使用 references 。T&
警告:请注意智能指针的成本。在内存或性能受限的环境中,仅使用普通指针和更手动的方案来管理内存可能是有益的。
费用:
- 如果您有自定义删除程序(例如,您使用分配池),那么这将产生每个指针的开销,而手动删除可以很容易地避免这些开销。
std::shared_ptr
具有复制时引用计数递增的开销,加上销毁时的递减,然后是删除保留对象的 0 计数检查。根据实现的不同,这可能会使代码膨胀并导致性能问题。- 编译时。与所有模板一样,智能指针对编译时间有负面影响。
例子:
struct BinaryTree
{
Tree* m_parent;
std::unique_ptr<BinaryTree> m_children[2]; // or use std::array...
};
二叉树不拥有其父树,但树的存在意味着其父树(或根树)的存在,因此使用普通指针。二叉树(具有值语义)对其子树具有唯一的所有权,因此它们是 .nullptr
std::unique_ptr
struct ListNode
{
std::shared_ptr<ListNode> m_next;
std::weak_ptr<ListNode> m_prev;
};
在这里,列表节点拥有它的 next 和 previous 列表,因此我们定义了一个方向,并使用 next 和 for prev 来打破循环。shared_ptr
weak_ptr
评论
shared_ptr<BinaryTree>
weak_ptr<BinaryTree>
共享所有权:
采用的标准与 Boost 标准几乎相同。当您需要共享资源并且不知道哪一个会是最后一个活着时,请使用它们。用于观察共享资源而不影响其生命周期,而不是中断循环。循环通常不应该发生 - 两个资源不能相互拥有。shared_ptr
weak_ptr
weak_ptr
shared_ptr
请注意,Boost 还提供 shared_array
,这可能是 的合适替代品。shared_ptr<std::vector<T> const>
接下来,Boost 提供intrusive_ptr
,如果您的资源已经提供引用计数管理,并且您希望将其采用 RAII 原则,则这是一种轻量级解决方案。这个没有被标准采用。
唯一所有权:
Boost 还有一个不可复制的scoped_ptr
,您不能为其指定删除程序。 正在服用类固醇,当您需要智能指针时,应该是您的默认选择。它允许您在其模板参数中指定删除器,并且是可移动的,这与 .它也可以完全用于 STL 容器,只要您不使用需要可复制类型的操作(显然)。std::unique_ptr
boost::scoped_ptr
boost::scoped_ptr
再次注意,Boost 有一个数组版本:scoped_array
,该标准通过要求部分专用化来统一指针而不是 ing(使用 r)。 还提供而不是 和 .std::unique_ptr<T[]>
delete[]
delete
default_delete
std::unique_ptr<T[]>
operator[]
operator*
operator->
请注意,它仍在标准中,但已弃用。std::auto_ptr
§D.10 [depr.auto.ptr]
类模板已弃用。[ 注意:类模板 (20.7.1) 提供了更好的解决方案。——尾注
auto_ptr
unique_ptr
]
无所有权:
对资源的非所有权引用使用哑指针(原始指针)或引用,并且当您知道资源将比引用对象/范围的寿命更长时。当您需要可空性或可重置性时,首选引用并使用原始指针。
如果您想要对资源的非拥有引用,但您不知道该资源是否会比引用它的对象更长,请将资源打包到 a 中并使用 - 您可以测试父项是否处于活动状态,如果资源仍然存在,它将返回非 null 的 a。如果要测试资源是否已失效,请使用 .这两者听起来可能很相似,但在面对并发执行时却大不相同,因为只保证该语句的返回值。一个看似无辜的测试,比如shared_ptr
weak_ptr
shared_ptr
lock
shared_ptr
expired
expired
if(!wptr.expired())
something_assuming_the_resource_is_still_alive();
是一种潜在的争用条件。
评论
shared_ptr
weak_ptr
shared_array<T>
shared_ptr<T[]>
shared_ptr<vector<T>>
何时使用案例:unique_ptr
- 工厂方法
- 作为指针的成员(包括 pimpl)
- 将指针存储在 stl containter 中(以避免移动)
- 使用大型局部动态对象
何时使用案例:shared_ptr
- 跨线程共享对象
- 一般共享对象
何时使用案例:weak_ptr
- 用作一般参考的大映射(例如,所有打开的套接字的映射)
随意编辑和添加更多内容
评论