在现代C++中使用非智能指针

using non-smart pointers in modern C++

本文关键字:智能 指针 C++      更新时间:2023-10-16

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

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

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

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

  • 要么进入shared_ptr是否应该共享所有权,
  • 或者,如果所有权由范围(块或对象的生存期)确定,则进入unique_ptr

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

  • 原始指针(如果可能null),(由get获得)
  • 参考文献(如果可能不null,(由get获得)
  • 如果调用的目的是转移所有权,则按值unique_ptr s。(在这种情况下,您需要移动它们)

工厂方法应按值返回 unique_ptr s。(因为这样,如果不分配工厂方法的返回值,则对象会立即取消分配)

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

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

简短的回答:

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

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

长答案:

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

  • 有关使用遗留代码的建议

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

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

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

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

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

Sean Parent关于如何避免/减少指针使用的有趣演讲是:

  • 继承是邪恶的基础阶级

  • 基于价值语义和概念的多态性

希望这有帮助。

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

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

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

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

当然,

在现代C++中也有原始指针的用例:

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

当然,这些都是相当罕见的情况,到目前为止,指针的大多数用例智能指针对于新代码来说应该很好,但是:

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

不要重构代码,这

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

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

如果重构将节省的时间多于仅代码库

的某些部分的成本,请考虑仅重构代码库的这些部分。