智能指针的使用

3

我有一个项目,希望能更好地使用智能指针。

主要想法是在从函数返回新对象时使用它们。问题是要使用哪种智能指针?auto_ptr还是来自boost的shared_ptr?据我所知,auto_ptr速度较慢,但可以回退到“纯”指针。

如果在不需要的地方使用智能指针,会使性能变慢吗?


不要根据感知的效率来选择智能指针(因为人类在感知效率方面非常糟糕,通常会出错)。选择展示您期望指针使用方式的智能指针。如果您正在转移所有权,则auto_ptr很好;如果您正在创建一个隐式共享对象,则shared_ptr更好。 - Martin York
5个回答

12
你认为 auto_ptrshared_ptr 慢的原因是什么?通常情况下,我会期望相反的结果,因为shared_ptr 需要更新引用计数。
至于使用哪个智能指针,不同的智能指针意味着不同的所有权语义。所有权意味着在不再需要对象时负责删除它。
  • 裸指针意味着没有所有权; 使用智能指针正确的程序仍然可能在许多地方使用裸指针,其中并未打算拥有所有权(例如,如果您需要将一个可选对象的引用传递到函数中,您通常会使用裸指针)。
  • scoped_ptr 意味着单一(即非共享),不可转让所有权。
  • auto_ptr 意味着单一(即非共享)可转让所有权。这是我用来从函数返回新构造对象的智能指针(函数将对象转让给其调用者)。由于当定义auto_ptr时的语言限制,它难以正确使用,因此auto_ptr存在缺点(尽管智能指针具有单一,可转让所有权语义的预期目的既有效又有用,但它已经拥有了非常糟糕的声誉)。
  • unique_ptr 具有与auto_ptr相同的语义,但使用新的C++0x特性(右值引用),使其比auto_ptr更安全(不容易使用不正确)。如果您正在开发支持unique_ptr的平台,则应使用它而不是auto_ptr
  • shared_ptr 意味着共享所有权。在我看来,这被过度使用了。它确实有许多有效的用途,但不应简单地将其用作默认选项。
我还要补充一点,由于其他智能指针类在STL容器中无法实现其预期功能(由于容器内部的值复制),因此shared_ptr经常与STL容器一起使用。这经常导致在不需要共享所有权的情况下使用shared_ptr。在这些情况下,建议(如果可能)使用boost指针容器类(ptr_vectorptr_map等),它们提供了可转移但单个(非共享)所有权的容器通常所需的语义。
你应该始终考虑对象的所有权:在大多数情况下,通过清晰的系统设计,每个对象都有一个明显的所有者,不需要共享所有权。这有一个优点,那就是很容易看到对象将在何时何处被释放,不需要引用计数开销。
[编辑以注明新的unique_ptr]

无论如何,仍然存在一些干净的场景,其中 shared_ptr 很有用。我有一个线程框架,在其中线程信息存储在 shared_ptr 中,一个副本被给予线程对象本身,并在线程结束时释放,另一个副本被给予调用者。这是确保线程结束后进行清理,同时允许调用者安全地跟踪线程当前状态的最清洁的方式。 - mmmmmmmm
可能“在大多数情况下,具有清晰的系统设计”太过强调了。真正的共享所有权情况并不是那么罕见(尽管比单一所有权要少得多)。感谢您提供了一个很好的例子。 - John Bartholomew

2
你可能应该使用shared_ptr<>。如果不知道你想要做什么,很难给出更具体的建议。最好阅读其文档,看它是否符合你的需求。
性能差异很可能是可以忽略不计的。只有在极端情况下,例如每秒复制这些指针数百万次时,才可能会有明显的影响。

性能差异不可忽略。因为shared_ptr的开销不仅仅是引用计数的增加和减少。每个对象还需要进行第二次new/delete以维护信息!由于堆栈速度较慢,这在某些情况下可能会很糟糕。此外,如果存在循环引用,则shared_ptr可能会导致内存泄漏(可以通过使用weak_ptr来解决;但您需要考虑更多!)。 - mmmmmmmm
@rstevens:你可以使用make_shared(),它只进行一次分配。无论如何,通常性能瓶颈不在指针中,我怀疑OP的关注点是过早优化。此外,无论您使用什么类型的智能指针或非智能指针,都必须考虑内存泄漏。如果有什么问题,shared_ptr/weak_ptr使处理更容易。 - sth

1

我更喜欢使用shared_ptr,auto_ptr可能会引起很多麻烦,而且它的使用并不太直观。如果您希望将此对象插入STL容器中,那么肯定要使用shared_ptr。

关于性能方面,您需要付出一些代价,但这是微不足道的,大部分时间您可以忽略它。


1
性能成本可能非常高!请确保在使用智能指针后进行分析,以确定影响是否可接受。 - RedX

1

这要看情况。
相比于auto_ptr,shared_ptr有更好的用途,因为auto_ptr在赋值时会改变所有权,这是不寻常的特性。
此外,auto_ptr不能用于容器中。
如果你不想转移所有权,也不能将auto_ptr用作返回值。
智能指针的所有优点都被shared_ptr继承了,重载了相关运算符以像指针一样操作,并且可以用于容器中。 话虽如此,使用它们并不便宜。
你必须分析自己的需求,决定是否通过避免shared_ptr实现开销来获得实际收益。


错误:auto_ptr 可以在容器中使用...因为它的粗糙特性...(正是由于在容器中使用才有这些特性!!!) - mmmmmmmm
@rstevens:我不知道你的评论是什么意思。auto_ptr不符合标准容器元素的最基本要求之一。在复制或赋值后,auto_ptr源和接收端不是等价的。因此,在标准容器中不应使用auto_ptr - Cratylus
@user384706:auto_ptr被设计成可用于标准容器,如std::vector和std::list。它可能在其他容器中引起问题,但由于std::auto_ptr和std::-Containers是一起开发的,它们保证可以一起使用。 - mmmmmmmm
@rstevens:禁止使用auto_ptr的容器(根据标准委员会的规定)。 试图使用它们的代码不应该编译。但是,有STL实现(错误地)没有拒绝这种情况。仅仅因为auto_ptr是标准库的一部分,并不意味着它们能够正常地与STL容器配合使用。那是完全错误的!! - Cratylus

0

只使用shared_ptr。使用auto_ptr时,您只能有一个引用指向对象。而且,auto_ptr并不比shared_ptr慢,它必须比shared_ptr更快。

要避免这样的问题,您需要了解智能指针的工作原理。

auto_ptr只是存储指向对象的指针,并在其析构函数中销毁它。

auto_ptr的问题在于,当您尝试复制它时,它停止指向您的对象。

例如

auto_ptr a_ptr(new someClass);

auto_ptr another_ptr = aptr; //之后,another_ptr指向您的类,但a_ptr不再指向它!

这就是为什么我不建议您使用auto_ptr的原因。

共享指针计算有多少智能指针指向您的对象,并在没有指向它的指针时销毁您的对象。这就是为什么您可以有多个指向对象的指针。

但是,共享指针也不完美。如果在您的程序中存在循环图(当您有类A和B时,A具有指向B的成员shared_ptr,而B具有或B的成员对象具有指向A的shared_ptr),则A和B将永远不会被删除,您将有内存泄漏。

使用 shared_ptr 编写正确的代码需要小心并且使用 weak_ptr。 要了解更多信息,请查看这里 http://www.boost.org/doc/libs/1_45_0/libs/smart_ptr/smart_ptr.htm


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接