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

我在几篇文章中读到几乎永远不会使用原始指针。 相反,他们应该总是包装在智能指针内,无论是范围还是共享指针。

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

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

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

如果您正在编写一个仅包含头文件的库,则可以将智能指针和标准容器传递给您的内容。 它们的源代码在编译时可用于你的库,所以你只依靠它们的接口的稳定性,而不是它们的实现。

但是由于缺less标准的ABI,通常不能在模块边界上安全地传递这些对象。 GCC的shared_ptr可能与MSVC的shared_ptr不同,它也可以不同于Intel的shared_ptr 。 即使使用相同的编译器,这些类也不能保证在版本之间是二进制兼容的。

底线是,如果你想分发你的库的预build版本,你需要一个标准的ABI依赖。 C没有一个,但是编译器厂商对于给定平台的C库之间的互操作性非常好 – 事实上有标准。

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

然而,非库代码应该通常优先于原始指针。

可以有很多原因。 列举其中的几个:

  1. 智能指针成为最近标准的一部分。 直到他们成为其他图书馆的一部分
  2. 他们的主要用途是避免内存泄漏; 许多图书馆没有自己的memory management; 通常他们提供实用程序和API
  3. 它们被实现为包装,因为它们实际上是对象而不是指针。 与原始指针相比,其中有额外的时间/空间成本; 图书馆的用户可能不希望有这样的开销

编辑 :使用智能指针是一个完全开发人员的select。 这取决于各种因素。

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

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

Edit2由于下面的通道,在24小时的范围内有几个downvotes的string。 我不明白为什么答案是downvoted,即使下面只是一个附加的build议,而不是一个答案。
但是,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 } 

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

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

默认的智能指针,因为它是免费的,是unique_ptr 。 不幸的是,它需要C ++ 11移动语义,最近才出现。 所有其他智能指针都有一个代价( shared_ptrintrusive_ptr )或者具有不足理想的语义( auto_ptr )。

随着C + + 11在angular落,带来一个std::unique_ptr ,人们会想,它终于结束了…我不那么乐观。

只有less数几个主要的编译器实现了大部分的C ++ 11,并且只在最近的版本中实现。 我们可以预期,像QT和Boost这样的主stream图书馆会愿意保持与C ++ 03的兼容性,这有些排除了新的shiny智能指针的广泛采用。

您不应该远离智能指针,特别是在需要传递对象的应用程序中使用它们。

图书馆往往只是返回一个值或填充一个对象。 他们通常不需要在很多地方使用对象,所以他们不需要使用智能指针(至less不在他们的界面中,他们可以在内部使用它们)。

我可以举一个我们一直在努力的图书馆,经过几个月的发展,我意识到我们只在几个class级(所有class级的3-5%)中使用了指针和智能指针。

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

编辑 (我不能评论,因为我的名誉):通过引用传递variables是非常灵活的:如果你希望对象是只读的,你可以使用一个const引用(你仍然可以做一些讨厌的强制转换来写对象),但是可以获得最大限度的保护(与智能指针相同)。 但我确实同意返回对象更好。

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

至于Boost,我完全期望在适当的地方使用智能指针。 你可能要更具体。

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

好问题。 我不知道你所提到的具体的文章,但我不时读到类似的东西。 我的怀疑是这些文章的作者倾向于对C ++风格的程序devise产生偏见。 如果编写者只用C ++编写程序,然后尽快返回到Java或者其他语言,那么他就不会真正分享C ++的思想。

有人怀疑某些或大部分相同的作者更喜欢垃圾收集内存pipe理器。 我不这样做,但我觉得与他们不同。

智能指针是伟大的,但他们必须保持参考计数。 参考计数的保留需要花费 – 通常是适度的成本,但是在运行时还是要付出代价的。 通过使用裸指针保存这些成本没有任何问题,特别是如果指针由析构函数pipe理的话。

关于C ++的优秀之一是它支持embedded式系统编程。 裸指针的使用是其中的一部分。

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

进一步更新:下面的评论线程是照亮。 所有这一切都是推荐阅读。

还有其他types的智能指针。 你可能需要一个专门的智能指针来进行networking复制(检测是否被访问,并将任何修改发送到服务器等),保存更改历史logging,标记它被访问的事实,以便可以调查时将数据保存到磁盘等等。 不知道如果在指针中这样做是最好的解决scheme,但是在库中使用内置的智能指针types可能会导致人们被locking,从而失去了灵活性。

人们可以有各种不同的内存pipe理要求和解决scheme,而不是智能指针。 我可能想自己pipe理内存,我可以为内存池中的内容分配空间,以便事先分配空间,而不是在运行时(对游戏有用)。 我可能正在使用垃圾收集C ++实现(C ++ 11使这成为可能,尽pipe目前还不存在)。 或者,也许我只是没有做任何先进的事情,担心会打扰他们,我可以知道,我不会忘记未初始化的对象,等等。 也许我只是有信心在没有指针拐杖的情况下pipe理内存。

与C集成也是另一个问题。

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

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

如果我要写一个表单应用程序,我可能会考虑使用智能指针; 但是一旦内存pipe理是第二性的,没有粒度控制的内存变得安静烦人。