为什么C ++库和框架从不使用智能指针?

时间:2012-04-26 13:31:11

标签: c++ api pointers smart-pointers

我在一些文章中读到几乎不会使用原始指针。相反,它们应该始终包含在智能指针中,无论它是作用域还是共享指针。

然而,我注意到像Qt,wxWidgets和像Boost这样的库这样的框架永远不会返回也不会期望智能指针,就好像他们根本没有使用它们一样。相反,他们返回或期望原始指针。有什么理由吗?当我编写公共API时,我应该远离智能指针,为什么?

只是想知道为什么在许多重大项目似乎避免使用智能指针时会这样做。

8 个答案:

答案 0 :(得分:123)

除了许多库是在标准智能指针出现之前编写的,最大的原因可能是缺少标准的C ++应用程序二进制接口(ABI)。

如果您正在编写仅限标题的库,则可以将智能指针和标准容器传递给您的内容。它们的源代码在编译时可供您的库使用,因此您只依赖于其接口的稳定性,而不是它们的实现。

但由于缺乏标准的ABI,您通常无法跨模块边界安全地传递这些对象。 GCC shared_ptr可能与MSVC shared_ptr不同,后者也可能与英特尔shared_ptr不同。即使使用相同的编译器,也不能保证这些类在版本之间是二进制兼容的。

最重要的是,如果您要分发库的预构建版本,则需要一个可依赖的标准ABI。 C没有,但编译器供应商非常擅长给定平台的C库之间的互操作性 - 事实上有标准。

这种情况对C ++来说并不好。各个编译器可以处理它们自己的二进制文件之间的互操作,因此您可以选择为每个受支持的编译器(通常是GCC和MSVC)分发版本。但鉴于此,大多数库只是导出一个C接口 - 这意味着原始指针。

然而,非库代码通常更喜欢智能指针而不是原始代码。

答案 1 :(得分:40)

可能有很多原因。列出其中几个:

  1. 最近智能指针成为标准的一部分。直到那时他们 是其他图书馆的一部分
  2. 它们的主要用途是避免内存泄漏;很多图书馆 没有自己的记忆管理;通常他们提供 实用程序和API
  3. 它们被实现为包装器,因为它们实际上是对象而不是指针。与原始指针相比,其具有额外的时间/空间成本;库的用户可能不希望有这样的开销
  4. 编辑:使用智能指针是完全开发人员的选择。这取决于各种因素。

    1. 在性能关键系统中,您可能不想使用智能 产生开销的指针

    2. 需要向后兼容的项目,您可能不需要 使用具有C ++ 11特定功能的智能指针

    3. Edit2 因为下面的段落,在24小时内有一串几个downvotes。我不明白为什么答案被低估了,尽管下面只是一个附加建议而不是答案 但是,C ++总是方便您打开选项。 :)例如。

      template<typename T>
      struct Pointer {
      #ifdef <Cpp11>
        typedef std::unique_ptr<T> type;
      #else
        typedef T* type;
      #endif
      };
      

      在您的代码中使用它:

      Pointer<int>::type p;
      

      对于那些说智能指针和原始指针不同的人,我同意这一点。上面的代码只是一个的想法,其中一个人可以编写一个可以与#define互换的代码,这不是强制;

      例如,必须显式删除T*,但智能指针不能删除。我们可以使用模板Destroy()来处理它。

      template<typename T>
      void Destroy (T* p)
      {
        delete p;
      }
      template<typename T>
      void Destroy (std::unique_ptr<T> p)
      {
        // do nothing
      }
      

      并将其用作:

      Destroy(p);
      

      同样,对于原始指针,我们可以直接复制它,对于智能指针,我们可以使用特殊操作。

      Pointer<X>::type p = new X;
      Pointer<X>::type p2(Assign(p));
      

      Assign()的位置为:

      template<typename T>
      T* Assign (T *p)
      {
        return p;
      }
      template<typename T>
      ... Assign (SmartPointer<T> &p)
      {
        // use move sematics or whateve appropriate
      }
      

答案 2 :(得分:35)

智能指针有两个问题(前C ++ 11):

  • 非标准,所以每个图书馆都倾向于重新发明自己的(NIH综合症和依赖性问题)
  • 潜在成本

默认智能指针,因为它是免费的,unique_ptr。不幸的是,它需要C ++ 11移动语义,它只出现在最近。所有其他智能指针都有成本(shared_ptrintrusive_ptr)或具有不太理想的语义(auto_ptr)。

随着C ++ 11的临近,带来一个std::unique_ptr,人们会想到它终于结束了......我不是那么乐观。

只有少数几个主要编译器实现了大部分C ++ 11,并且仅在最近的版本中实现。我们可以期待像QT和Boost这样的主要库愿意保留与C ++ 03的兼容性一段时间,这在一定程度上排除了新的闪亮智能指针的广泛采用。

答案 3 :(得分:12)

你不应该远离智能指针,它们尤其适用于必须传递物体的应用程序。

库往往只返回一个值或填充一个对象。它们通常不具有需要在很多地方使用的对象,因此不需要它们使用智能指针(至少不在它们的界面中,它们可以在内部使用它们)。

我可以把我们一直在研究的库作为例子,经过几个月的开发后,我意识到我们只在几个类中使用了指针和智能指针(占所有类的3-5%)。

通过引用传递变量在大多数地方已经足够了,每当我们有一个可能为null的对象时我们使用智能指针,而当我们使用的库强制我们时,我们使用原始指针。

修改(由于我的声誉,我无法发表评论): 通过引用传递变量是非常灵活的:如果你想要只读对象你可以使用const引用(你仍然可以做一些令人讨厌的强制转换以便能够写入对象)但你可以得到最大的保护(它是相同的用智能指针)。 但我确实同意返回对象更好。

答案 4 :(得分:9)

Qt毫无意义地重新发明了标准库的许多部分,试图成为Java。我相信它现在确实有自己的智能指针,但总的来说,它几乎不是设计的顶峰。据我所知,wxWidgets是在编写可用的智能指针之前设计的。

至于Boost,我完全希望他们在适当的地方使用智能指针。你可能必须更具体。

此外,不要忘记存在强制所有权的智能指针。如果API没有所有权语义,那么为什么要使用智能指针?

答案 5 :(得分:3)

好问题。我不知道您提到的具体文章,但我不时阅读类似的内容。我怀疑这些文章的作者往往对C ++风格的编程抱有偏见。如果作者只在他必须的时候用C ++编程,那么尽快回到Java或者这样,那么他就不会真正分享C ++的思维方式。

有人怀疑一些或大多数相同的编写者更喜欢垃圾收集内存管理器。我没有,但我的想法与他们不同。

智能指针很棒,但它们必须保持引用计数。保持参考计数可以承担成本 - 通常是适度的成本,但仍然是成本 - 在运行时。使用裸指针节省这些成本没有任何问题,特别是如果指针由析构函数管理。

C ++的优点之一是它对嵌入式系统编程的支持。使用裸指针是其中的一部分。

更新:评论者已正确观察到C ++的新unique_ptr(自TR1起可用)不计算引用。评论者对“智能指针”的定义也与我的想法不同。他对这个定义可能是正确的。

进一步更新:下面的评论帖子很有启发性。所有这些都是推荐阅读。

答案 6 :(得分:2)

还有其他类型的智能指针。您可能需要一个专门的智能指针,用于网络复制(一个检测它是否被访问并向服务器发送任何修改或其他一些),保留更改历史,标记它被访问的事实,以便可以调查时您将数据保存到磁盘等。不确定在指针中执行此操作是否是最佳解决方案,但在库中使用内置的智能指针类型可能会导致人们被锁定并失去灵活性。

人们可以拥有除智能指针之外的各种不同的内存管理要求和解决方案。我可能想要自己管理内存,我可以为内存池中的事物分配空间,因此它是事先分配的,而不是在运行时分配的(对游戏很有用)。我可能正在使用垃圾收集的C ++实现(C ++ 11使这成为可能,虽然还不存在)。或者也许我只是没有做足够高级的事情而担心打扰他们,我可以知道我不会忘记未初始化的对象等等。也许我对没有指针拐杖管理记忆的能力充满信心。

与C的整合也是另一个问题。

另一个问题是智能指针是STL的一部分。 C ++可以在没有STL的情况下使用。

答案 7 :(得分:-1)

这还取决于你在哪个领域工作。我以游戏引擎为生,我们避免像瘟疫这样的提升,在游戏中,提升的开销是不可接受的。在我们的核心引擎中,我们最终编写了自己的stl版本(很像ea stl)。

如果我要编写表单应用程序,我可能会考虑使用智能指针;但是一旦内存管理成为第二天性,对内存的细微控制就会变得安静烦人。