关于c ++:指针,智能指针或共享指针?

Pointers, smart pointers or shared pointers?

本问题已经有最佳答案,请猛点这里访问。

我正在用普通指针编程,但是我听说过一些库,比如Boost实现智能指针。我还看到,在Ogre3D渲染引擎中,有大量使用共享指针。

这三者到底有什么区别,我是否应该坚持只使用其中的一种?


Sydius很好地概述了这些类型:

  • 正常的指针就是——它们指向内存中的某个地方。谁拥有它?只有评论才能让你知道。谁解放了它?希望业主在某个时候。
  • 智能指针是一个涵盖多种类型的总括术语;我假设您指的是使用RAII模式的作用域指针。它是一个堆栈分配的对象,包装一个指针;当它超出范围时,它调用它包装的指针上的delete。它"拥有"所包含的指针,因为它负责在某个点删除它。它们允许您获取对它们包装的指针的原始引用,以便传递给其他方法,并释放指针,允许其他人拥有它。复制它们没有意义。
  • 共享指针是一个堆栈分配的对象,它包装一个指针,这样您就不必知道谁拥有它。当内存中对象的最后一个共享指针被销毁时,包装好的指针也将被删除。

你应该什么时候用呢?您将大量使用作用域指针或共享指针。您的应用程序中运行了多少线程?如果答案是"潜在的很多",那么如果在任何地方使用共享指针,可能会成为性能瓶颈。原因是创建/复制/销毁共享指针需要一个原子操作,如果有许多线程在运行,这可能会妨碍性能。然而,情况并非总是如此——只有测试才能肯定地告诉您。

有一个(我喜欢的)反对共享指针的论点——通过使用它们,程序员可以忽略谁拥有指针。这可能导致循环引用(Java将检测这些,但共享指针不能)或一般程序员懒惰在一个大的代码基础的棘手情况。

使用作用域指针有两个原因。第一个是为了简单的异常安全和清理操作-如果您想确保对象被清理,不管面对异常是什么,并且您不想堆栈分配该对象,请将它放在一个作用域指针中。如果操作成功,您可以自由地将其转移到共享指针,但同时使用范围指针节省开销。

另一种情况是,当您需要明确的对象所有权时。有些团队喜欢这样,有些则不喜欢。例如,数据结构可能返回指向内部对象的指针。在作用域指针下,它将返回一个应被视为弱引用的原始指针或引用-在销毁拥有该指针的数据结构后访问该指针是一个错误,删除该指针是一个错误。在共享指针下,如果仍有人持有某个句柄,则所属对象无法破坏它返回的内部数据-这可能会使资源打开的时间比需要的时间长得多,或者更糟,具体取决于代码。


术语"智能指针"包括共享指针、自动指针、锁定指针和其他指针。你的意思是说自动指针(更模糊地说是"拥有指针"),而不是智能指针。

哑指针(t*)永远不是最佳解决方案。它们使您做显式内存管理,这是冗长的,容易出错,有时几乎不可能。但更重要的是,它们并没有表明你的意图。

自动指针删除销毁时的指针。对于数组,更喜欢像vector和deque这样的封装。对于其他对象,很少需要将它们存储在堆中——只需要使用局部变量和对象组合。但是,对于返回堆指针的函数(如工厂和多态返回),仍然需要自动指针。

共享指针在最后一个指向它的共享指针被破坏时删除该指针。当您需要一个更简单的开放式存储方案时,这是非常有用的,在该方案中,预期的生存期和所有权可能因情况而变化很大。由于需要保留一个(原子)计数器,它们比自动指针慢一点。有些人开玩笑地说,共享指针是为那些不能设计系统的人设计的——为自己判断。

对于共享指针的基本对应项,也可以查找弱指针。


智能指针将在超出范围后进行自我清理(从而消除对大多数内存泄漏的恐惧)。共享指针是智能指针,用于统计指针存在的实例数,并且仅在计数为零时清理内存。一般来说,只使用共享指针(但一定要使用正确的类型——数组有不同的类型)。他们和拉伊有很多关系。


为了避免内存泄漏,您可以随时使用智能指针。C++中基本上有2种不同类型的智能指针。

  • 已计数引用(例如boost::shared_ptr/std::tr1:shared_ptr)
  • 非引用计数(例如boost::scoped_ptr/std::auto_ptr)

主要区别在于,引用计数的智能指针可以被复制(并在std::containers中使用),而作用域指针则不能。非引用计数指针几乎没有开销,或者根本没有开销。引用计数总是引入某种开销。

(我建议避免自动驾驶,如果使用不当会有严重的缺陷)


为了给Sydius的答案增加一点内容,智能指针通常会通过捕获许多容易出错的错误来提供更稳定的解决方案。原始指针将具有一些性能优势,在某些情况下更灵活。在链接到某些第三方库时,也可能会强制使用原始指针。