【问题标题】:Why shouldn't I use shared_ptr and unique_ptr always and instead use normal pointers?为什么我不应该总是使用 shared_ptr 和 unique_ptr 而是使用普通指针?
【发布时间】:2013-12-17 09:23:03
【问题描述】:

我有 C#obj-c 的背景,所以 RC/GC 是我(仍然)珍视的东西。当我开始更深入地学习 C++ 时,我不禁想知道为什么我会在它们如此 unmanaged 的情况下使用普通指针而不是其他替代解决方案?

shared_ptr 提供了一种存储引用的好方法,并且在不删除它们的情况下不会丢失它们的轨道。我可以看到 normal 指针的实用方法,但它们似乎只是不好的做法。

有人可以说明这些替代方案吗?

【问题讨论】:

  • 你不能不假思索地在任何地方使用shared_ptr(相反,你可以对所有东西使用GC管理的引用),因为当你不可避免地创建循环引用时你会傻眼了。当您编写 C++ 程序时,您必须考虑所有权。只是没有办法解决它。这并不是说你不应该使用它们(你应该),但选择正确的并不是一件容易的事。
  • 另外,复制shared_ptr 以及让它超出范围会产生明显的开销,因为它在最好的情况下需要原子操作,在最坏的情况下会锁定(标准保证这是线程-安全的)。复制原始指针几乎是无操作的。
  • @Damon 实际上,在 C++ 中,您可能比在 C# 中更少考虑所有权,因为您应该使用值语义(并且您不必考虑谁拥有对象堆)。我在 Java 中遇到问题的原因是所有权(从最大的意义上讲)不清楚。 (例如,谁“拥有”java.awt.Component.getSize() 返回的java.awt.Dimension?)
  • 所以 unmanaged 指针,是的,时间过得很快。

标签: c++ shared-ptr unique-ptr


【解决方案1】:

当然,如果他们拥有指针,我们鼓励您使用共享和唯一的 ptr。但是,如果您只需要一个观察者,那么原始指针就可以了(指针对其指向的任何内容不承担任何责任)。

std::uniqe_ptr 基本上没有开销,std::shared_ptr 有一些开销,因为它会为您进行引用计数,但您很少需要在这里节省执行时间。

此外,如果您可以通过设计保证生命周期/所有权层次结构,则不需要“智能”指针;比如说树中的父节点比它的子节点寿命更长——尽管这与指针是否真正拥有某些东西这一事实略有关系。

【讨论】:

    【解决方案2】:

    正如其他人提到的,在 C++ 中,您必须考虑所有权。话虽如此,我目前正在开发的 3D 联网多人 FPS 有一条官方规则,称为“不更新也不删除”。它仅使用共享的和唯一的指针来指定所有权,并在我们需要与 C API 交互的任何地方从它们中检索到原始指针(使用.get())。性能损失并不明显。我以此为例来说明对性能的影响可以忽略不计,因为游戏/模拟通常具有最严格的性能要求。

    这也大大减少了调试和查找内存泄漏所花费的时间。理论上,一个设计良好的应用程序永远不会遇到这些问题。然而,在现实生活中,使用设计不佳的截止日期、遗留系统或现有游戏引擎,它们对于像游戏这样的大型项目来说是不可避免的……除非你使用智能指针。如果您必须动态分配,没有足够的时间来设计/重写架构或调试与资源管理相关的问题,并且您希望尽快启动它,那么智能指针是可行的方法并且不会产生即使在大型游戏中也有显着的性能成本。

    【讨论】:

    • 为什么要使用动态分配?如果是因为复制对象成本太高(游戏软件中很可能出现这种情况),那么可能会指示智能指针(可能是某种侵入性智能指针,而不是std::shared_ptr)。但在很多情况下,情况不应该如此。智能指针无法正确处理实体对象,例如:当Weapon 获得hitByDisintegratorBlast 事件时,它可能最终会执行delete this,这将使用智能指针。
    • 是的,这看起来确实很随意:)。
    • 我很好奇你认为你不会在游戏中动态分配对象?纹理、着色器、网格、动画、声音、音乐等都相当大,它们的所有权由多个其他资源共享。
    • @uberwulu 反应很好,我很好奇你用的是什么游戏引擎?
    【解决方案3】:

    问题恰恰相反:为什么要使用 smart 指针?与 C#(我认为是 Obj-C)不同,C++ 使 使用值语义,这意味着除非一个对象有 特定于应用程序的生命周期(在这种情况下,智能 指针适用),您通常会使用值语义,并且 没有动态分配。有例外,但如果你做到了 价值语义方面的思考点(例如,像 int),定义适当的复制构造函数和赋值 必要时的运算符,并且动态分配 除非对象具有外部定义的生命周期,否则您将 发现你很少需要做更多的事情;一切只是 照顾好自己。使用智能指针非常重要 大多数编写良好的 C++ 中的异常。

    【讨论】:

    • 您的回答不仅不正确,而且会误导新开发人员。您将经常需要使用动态对象,其中 N 来自配置文件或处理容器时。在大多数复杂的应用程序(如游戏)中,您可能会有对象漂浮在各处,并且一次是多个容器的成员。您根本不能对此使用按值语义。看看像虚幻引擎这样的大型代码库(写得很好的代码,IMO),你会一直看到大量动态分配的对象。
    【解决方案4】:

    有时您需要与 C API 进行互操作,在这种情况下您需要使用原始指针 至少对于代码的那些部分。

    【讨论】:

    • 哦,很好,这实际上漏掉了我的答案! :) 但是我用漂亮的 C++ 包装了这些,不要回头!
    【解决方案5】:

    在嵌入式系统中,指针通常用于访问特定地址的硬件芯片或内存的寄存器。

    由于硬件寄存器已经存在,因此无需动态分配它们。如果指针被删除或它的值改变,不会有任何内存泄漏。

    与函数指针类似。函数不是动态分配的,它们具有“固定”地址。重新分配函数指针或删除函数指针不会导致内存泄漏。

    【讨论】:

      猜你喜欢
      • 2011-07-31
      • 2013-01-30
      • 2016-02-18
      • 2010-10-12
      • 2016-05-23
      • 2013-12-07
      • 1970-01-01
      • 1970-01-01
      • 2021-12-31
      相关资源
      最近更新 更多