指针,智能指针或共享指针?

时间:2009-01-06 17:42:25

标签: c++ pointers

我使用普通指针进行编程,但我听说像Boost这样的库实现了智能指针。我还看到在Ogre3D渲染引擎中有很多共享指针的使用。

这三者之间究竟有什么区别,我应该坚持使用它们的类型吗?

5 个答案:

答案 0 :(得分:142)

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

  • 正常指针就是这样 - 它们指向内存中的某些东西。谁拥有它?只有评论会通知你。谁释放了它?希望在某些时候是主人。
  • 智能指针是一个涵盖多种类型的全面术语;我假设你的意思是使用RAII模式的范围指针。它是一个堆栈分配的对象,它包装了一个指针;当它超出范围时,它会在它包装的指针上调用delete。它“拥有”包含的指针,因为它负责在某个时候删除它。它们允许您获取对它们包装的指针的原始引用以传递给其他方法,以及释放指针,允许其他人拥有它。复制它们没有意义。
  • 共享指针是一个堆栈分配的对象,它包装指针,因此您不必知道谁拥有它。当销毁内存中对象的最后一个共享指针时,包装指针也将被删除。

你什么时候应该使用它们?您将大量使用作用域指针或共享指针。您的应用程序中运行了多少个线程?如果答案“可能很多”,如果在任何地方使用,共享指针可能会成为性能瓶颈。原因是创建/复制/破坏共享指针需要是原子操作,如果有许多线程在运行,这可能会影响性能。但是,情况并非总是如此 - 只有测试才会告诉您。

有一个参数(我喜欢)反对共享指针 - 通过使用它们,你允许程序员忽略谁拥有一个指针。这可能导致循环引用的棘手情况(Java将检测这些,但共享指针不能)或大型代码库中的一般程序员懒惰。

使用作用域指针有两个原因。第一个是简单的异常安全和清理操作 - 如果你想保证一个对象被清除,无论面对异常是什么,并且你不想堆栈分配该对象,把它放在一个范围指针中。如果操作成功,您可以随意将其转移到共享指针,但同时使用范围指针保存开销。

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

答案 1 :(得分:32)

术语“智能指针”包括共享指针,自动指针,锁定指针等。你的意思是说自动指针(更模糊地称为“拥有指针”),而不是智能指针。

哑指针(T *)永远不是最好的解决方案。它们使您进行明确的内存管理,这种内容管理冗长,容易出错,有时几乎不可能。但更重要的是,他们没有表明你的意图。

自动指针在销毁时删除指针。对于数组,更喜欢像vector和deque这样的封装。对于其他对象,很少需要将它们存储在堆上 - 只需使用本地和对象组合。返回堆指针的函数仍然需要自动指针 - 例如工厂和多态返回。

共享指针在销毁最后一个共享指针时删除指针。当您需要一个简单的开放式存储方案时,这非常有用,其中预期的生命周期和所有权可能会根据具体情况而有很大差异。由于需要保留(原子)计数器,它们比自动指针慢一点。有人说,半开玩笑,共享指针是为那些无法设计系统的人 - 自己判断。

对于共享指针的基本对应物,也要查找弱指针。

答案 2 :(得分:21)

智能指针会在超出范围后自行清理(从而消除对大多数内存泄漏的恐惧)。共享指针是智能指针,用于记录指针存在多少个实例,并且仅在计数达到零时清理内存。通常,只使用共享指针(但一定要使用正确的类型 - 数组有不同的类型)。它们与RAII有很大关系。

答案 3 :(得分:8)

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

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

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

(我建议避免使用auto_ptr,如果使用不当,它会有一些严重的缺陷)

答案 4 :(得分:5)

为了向Sydius的回答添加一点点,智能指针通常会通过捕获许多容易出错的错误来提供更稳定的解决方案。原始指针将具有一些性能优势,并且在某些情况下可以更灵活。在链接到某些第三方库时,您可能还被迫使用原始指针。