【问题标题】:best practice when returning smart pointers返回智能指针时的最佳实践
【发布时间】:2010-11-01 18:38:44
【问题描述】:

返回智能指针时的最佳做法是什么,例如 boost::shared_ptr?我应该按标准返回智能指针还是底层原始指针?我来自 C#,所以我倾向于总是返回智能指针,因为它感觉不错。像这样(跳过更短代码的 const 正确性):

class X
{
public:
    boost::shared_ptr<Y> getInternal() {return m_internal;}

private:
    boost::shared_ptr<Y> m_internal;
}

但是,我看到一些经验丰富的编码人员返回原始指针,并将原始指针放入向量中。正确的做法是什么?

【问题讨论】:

    标签: c++ boost smart-pointers


    【解决方案1】:

    没有“正确”的方式。这真的取决于上下文。

    您可以在内部使用智能指针处理内存,并在外部提供引用或原始指针。毕竟,您界面的用户不需要知道您如何在内部管理内存。在同步上下文中,这是安全且高效的。在异步上下文中,存在许多陷阱。

    如果您不确定该做什么,您可以安全地将智能指针返回给您的调用者。当引用计数达到零时,该对象将被释放。只要确保你没有一个类会永远保存对象的智能指针,从而防止在需要时释放。

    最后一点,在 C++ 中不要过度使用动态分配的对象。在许多情况下,您不需要指针并且可以处理引用和 const 引用。这样更安全,也减少了内存分配器的压力。

    【讨论】:

    • 您可以安全地返回shared_ptr(或weak_ptr,毫无疑问还有其他一些实现),但不能返回unique_ptr。 (嗯,你可以回unique_ptr&amp;&amp;。)
    • @MikeC:返回unique_ptr 是绝对安全的。事实上,返回unique_ptr&amp;&amp; 几乎总是不安全的。
    【解决方案2】:

    这要看指针是什么意思了。

    当返回一个 shared_pointer 时,您在语法上说“您将共享此对象的所有权”,这样,如果原始容器对象在您释放指针之前死亡,则该对象仍然存在。

    返回一个原始指针表示:“你知道这个对象,但不拥有它”。这是一种传递控制权的方式,但不会将生命周期与原始所有者联系起来。

    (在一些较旧的 c 程序中,这意味着“现在删除我是你的问题”,但我强烈建议避免使用这个)

    通常,默认共享为我省去了很多麻烦,但这取决于您的设计。

    【讨论】:

      【解决方案3】:

      我遵循以下准则将指针参数传递给函数并返回指针:

      boost::shared_ptr
      

      API 和客户端共享此对象的所有权。但是,如果对象代表某种图形,则必须小心避免使用 shared_ptr 进行循环引用。出于这个原因,我尝试限制我对shared_ptr 的使用。

      boost::weak_ptr / raw pointer
      

      API 拥有此对象,您可以在它有效时共享它。如果客户端有可能比我使用的 api 更长寿。

      std::auto_ptr
      

      API 正在创建一个对象,但客户端拥有该对象。这样可以确保返回的代码是异常安全的,并且清楚地表明所有权正在转移。

      boost::scoped_ptr
      

      用于指向存储在堆栈中或作为类成员变量的对象的指针。我尝试先使用scoped_ptr。

      像所有准则一样,有时规则会发生冲突或不得不改变,然后我会尝试使用智能。

      【讨论】:

      • 我更喜欢 boost::unique_ptr 而不是 std::auto_ptr 因为它更难意外转移所有权。
      • 谢谢我没有发现 unique_ptr,因为它不在 boost book 中。看起来不错。
      【解决方案4】:

      我通常返回来自工厂或类似的“拥有”/“唯一”智能指针,以明确谁负责清理。

      此示例https://ideone.com/qJnzva 展示了如何返回std::unique_ptr,当调用者为其赋值的变量的范围超出范围时,该std::unique_ptr 将被删除。

      虽然智能指针确实会删除自己的指针,但保存智能指针的变量的生命周期是 100% 由调用者控制的,因此调用者决定何时删除指针。然而,由于它是一个“唯一”且“拥有”的智能指针,没有其他客户端可以控制生命周期。

      【讨论】:

      • 嗯?为什么不加解释就否决?大型企业应用程序代码太少?
      • 为什么要投反对票?首先,我认为您对“独特”的使用不一定是指unique_ptr,但存在这样的野兽,您的回答令人困惑。如果您的意思是unique_ptr,那么您如何“返回”它? (正如我在上面的另一条评论中指出的那样,unique_ptr&amp;&amp; 将起作用。)其次,清理的责任在于智能指针本身,通过返回智能指针,您可以免除客户对清理的任何担忧.
      • @MikeC:我认为你使用“unique”并不一定意味着unique_ptr它不是,因此使用了“unique”。我的意思是任何表现得像std::unique_ptr。 但这样的野兽存在,你的答案令人困惑。它不是野兽,如果它令人困惑,我会尝试重新制定自己。
      • @MikeC: 如果你的意思是 unique_ptr,那么你如何“返回”它? 就像你对其他任何智能指针一样:ideone.com/qJnzva (正如我在上面的另一条评论中所指出的,unique_ptr&& 将起作用。) 如ideone.com/qJnzva 所示,它不是必需的。
      • @MikeC: 第二,清理的责任在于智能指针虽然这是真的,但清理与调用者的范围相关,这就是我的意思“明确谁负责清理。” - 调用者通过获取返回的智能指针的变量的生命周期来控制清理。 通过返回一个智能指针,您免除了客户对清理的任何担忧。 同样,清理仍然 100% 由调用者控制,因为调用者控制着生命周期/范围分配给变量。
      【解决方案5】:

      我永远不会返回一个原始指针,而是返回一个weak_ptr来告诉指针的用户他没有对资源的控制权。

      如果您返回一个weak_ptr,则应用程序中不太可能存在悬空指针。

      如果出现性能问题,我会返回对对象的引用和 hasValidXObject 方法。

      【讨论】:

        【解决方案6】:

        在我看来,在 C++ 中,您应该始终证明使用无保护指针的合理性。

        可能有很多正当理由:需要非常高的性能、非常低的内存使用量、处理遗留库,因为指针存储的底层数据结构存在一些问题。但是[动态分配的]指针有点“邪恶”,因为你必须在每一个可能的执行路径上释放内存,你几乎肯定会忘记一个。

        【讨论】:

        • 可以使用 goto 语句(在非 spagetti 模式下)确保每个退出点都经过一段代码... ergo:您可以滚动自己的 throw-try-catch-finally ...我们做到了...它工作得非常出色,而且没有 Java/C# 异常处理的大量开销。
        【解决方案7】:

        我不会将原始指针放在向量中。

        如果他们使用 auto_ptr 或 boost::scoped_ptr,他们只能使用(或返回)原始指针。我猜这可以解释他们的编码方式。

        【讨论】:

        • 我不明白你所说的“如果他们使用 auto_ptr 或 boost::scoped_ptr,他们不能使用(或返回)除了原始指针之外的任何东西。”你的意思是一个函数不能返回一个auto_ptr,还是说共享不能用auto_ptr来表达?
        • 我的意思是auto_ptr(或scoped_ptr)不能在容器中使用。
        【解决方案8】:

        取决于你的目标。

        盲目地将智能 ptr 返回到内部数据可能不是一个好主意(这对您要解决的任务非常敏感) - 您最好只提供一些 doX() 和 doY() 使用而是在内部使用指针。

        另一方面,如果返回智能 ptr,您还应该考虑当对象最终无法相互销毁时,您将不会创建相互循环引用(在这种情况下,weak_ptr 可能是更好的选择)。

        否则,就像上面已经提到的那样,性能/遗留代码/生命周期的考虑都应该考虑在内。

        【讨论】:

          【解决方案9】:

          const boost::shared_ptr &getInternal() {return m_internal;}

          这样可以避免复制。

          有时您希望返回参考,例如:

          • Y &operator*() { return *m_internal; }
          • const Y &operator*() const { return *m_internal; }

          仅当引用将被使用并立即丢弃时,这也很好。 原始指针也是如此。 返回一个weak_ptr 也是一种选择。

          根据目标,这 4 个很好。这个问题需要更广泛的讨论。

          【讨论】:

          • 不,永远不要返回对 shared_ptr 的引用。复制,或weak_ptr。复制shared_ptr (相当)便宜。
          • 不是引用,而是 const 引用。我的答案是正确的。它是安全的,性能比任何东西都好。
          猜你喜欢
          • 2014-05-30
          • 1970-01-01
          • 2015-04-02
          • 1970-01-01
          • 2020-09-14
          • 1970-01-01
          • 1970-01-01
          • 2023-03-16
          • 2022-06-15
          相关资源
          最近更新 更多