【问题标题】:using non-smart pointers in modern C++在现代 C++ 中使用非智能指针
【发布时间】:2013-10-17 03:08:56
【问题描述】:

短版:
在现代 C++ 中使用非智能指针有什么可接受的理由吗?

加长版:
我们有一个包含大量旧 C++ 代码的巨大产品,现在我们正试图将其重构为现代 C++ 时代。除了所有老式代码之外,还有大量指针传递(主要是使用 SAL 注释来提供某种安全感),我想知道我们是否应该将它们全部更改为智能指针,或者保留其中一些?
在尝试转换其中一些代码时,我最终得到了一个可以简单地证明使用智能指针的代码。

所以问题是:是否存在过度使用智能指针这样的事情?
或者换句话说:这些天对于非智能指针是否有任何可接受的场景?

【问题讨论】:

  • 如果他们不拥有任何东西,那就没有多大意义了。
  • 问题是它们可能根本不应该是指针(如你所说,原始指针在现代 c++ 中没有太多位置),但如果它没有坏,不要不要修复它
  • 如果原始指针不拥有任何资源,则它们在现代 c++ 中很好。这意味着您不必将所有原始指针都更改为智能指针。

标签: c++ pointers c++11 refactoring


【解决方案1】:

智能指针(unique_ptrshared_ptr)应该是 OWNING 指针(即负责对象的销毁)。使用它们的底线是new 创建的任何对象都应尽快推入unique_ptr,以防止内存泄漏。之后,unique_ptr 应该最终被移动:

  • 如果应共享所有权,则转为 shared_ptr
  • 如果所有权由范围(块的或对象的生命周期)确定,则转入unique_ptr

releases 应该很少见。如果您的代码传递非拥有指针,这些应该是:

  • 原始指针,如果它们可能是null,(由get 获得)
  • 引用可能不是null,(由get 获得)
  • unique_ptrs 如果通话的目的是转让所有权,则按价值计算。 (在这种情况下,您需要移动它们)

工厂方法应该按值返回unique_ptrs。 (因为那样的话,如果你不分配工厂方法的返回值,对象会立即被释放)

并查看 Ali 关于处理遗留代码的一些哲学观点的链接的答案。 (我完全同意)

【讨论】:

    【解决方案2】:

    短版:
    使用非智能是否有任何可接受的理由 现代 C++ 中的指针?

    简答:

    当然,如果他们只用于观察,也就是说,他们不拥有指针。但是,即使在这种情况下,也要尝试使用引用而不是指针;仅当您确实需要将指针设为可选时才使用指针(例如,使用 null_ptr 初始化,然后稍后重新分配)。

    加长版:
    我们有一个包含大量旧 C++ 代码的巨大产品,现在我们正试图将其重构为现代 C++ 时代。 [...]

    长答案:

    当我阅读这些行时,我想到了这个答案:

    我希望我能不止一次地支持这个答案。我会引用:"[...] 对于我们所做的每一个重构,我们都可以证明“这个特定的改变将使我们现在正在做的实际任务更容易”。而不是“这对于未来的工作来说现在更干净了'。”

    长话短说,除非真的需要,否则不要进行大的重构。

    所以问题是:是否存在过度使用智能指针这样的事情?

    在我看来,std::shared_ptr 被过度使用了。它使用起来非常舒适,它给您一种无需考虑所有权问题的错觉。但这还不是全部。我完全同意Sean Parent“共享指针与全局变量一样好。”共享指针也可能引入非常困难的所有权问题,等等。

    另一方面,如果您需要在堆上分配一些东西,请使用unique_ptr。如果你真的需要堆分配,你不能过度使用它。根据我的经验,使用 unique_ptr 还可以使代码更清晰、更易于理解,因为所有权问题变得不言自明。

    Sean Parent 就如何避免/减少指针的使用进行了有趣的讨论:

    希望这会有所帮助。

    【讨论】:

      【解决方案3】:

      是的,原始指针仍然可以用作“可选引用”。 IE。 T* 类似于 T&。两者都不意味着所有权,但T* 可以是nullptr

      【讨论】:

      • 但是肯定有比原始指针更安全的选择吗?我正在考虑 boost 的(现在也是 std::experimental 的)optional 类型,因为它很清楚发生了什么,但即使只是智能指针本身也可以以这种方式使用。
      • @JimmidyJoo:是的,我昨天做了exact same observation
      【解决方案4】:

      在此处查看演讲:http://channel9.msdn.com/Events/GoingNative/2013(尤其是 Stroustrup 的演讲)。

      简短的回答是否定的,假设“现代 C++”>= c++11

      答案并非总是如此,尝试重组一个大型项目几乎总是很困难。我们思考问题的方式受到我们解决问题的工具的限制。在许多情况下,当使用指针比尝试将基本逻辑重新表达为类和智能指针友好更有意义时,进行此类重构。我认为智能指针被过度使用的情况较少,而类使用不足的情况更多。 YMMV ;-)

      【讨论】:

      • 嗯“现代C++”是什么意思>= c++11
      • @aaronman “假设 'modern C++' OP 是指 C++11 或更高版本”
      • 嗯,在 C++03 中编写糟糕代码的借口仍然接近于零。
      【解决方案5】:

      当然,现代 C++ 中有原始指针的用例:

      • 必须可作为纯 C 编译的接口(尽管实现本身可能会使用那些 C++ 特性,但这些特性不属于 C 特性,例如类或智能指针)
      • 代码非常低级,非常低级,即使是最简单的智能指针也被证明是重量级的

      当然,这些是相当少见的情况,到目前为止,智能指针的大多数用例都适用于新代码,但是:

      如果现有代码在原始指针上运行良好,为什么要花时间重写它并冒险在使用版本将其转换为智能指针时添加错误?

      不要重构代码,这很好,只是因为新代码更好地遵循现代编程标准。这些编程标准并不是为了它们本身而存在,而是为了让一些代码的工作更容易,所以不要进行重构,这将花费你更多的时间,而不是它们在未来为你节省的时间。

      这意味着:如果要花费更多时间来检查,哪些指针可以安全地转换为智能指针,哪些不能安全地转换为您的重构可能引入的错误,那么您将由于重构,能够节省未来的维护工作,然后干脆不重构,让它保持原样。

      如果重构节省的时间多于代码库某些部分的成本,那么请考虑仅重构代码库的那些部分。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-04-29
        • 1970-01-01
        • 1970-01-01
        • 2011-08-12
        • 1970-01-01
        • 1970-01-01
        • 2011-04-06
        • 1970-01-01
        相关资源
        最近更新 更多