【问题标题】:Use of raw pointers in modern C++ post C++11 [closed]在现代 C++ 后 C++11 中使用原始指针 [关闭]
【发布时间】:2014-10-31 13:27:15
【问题描述】:

鉴于现在大多数体面的编译器都很好地支持 C++11 标准,2014 年使用原始指针的主要原因是什么?

我确定了几个场景:

  1. 您正在扩展大量使用原始指针的遗留代码库,并且您希望保持样式的一致性。

  2. 您正在使用仅导出原始指针的库,但我想您仍然可以使用强制转换。

  3. 您希望利用指针的能力来提供多级间接。 (我不太了解 C++11,不知道这是否可以使用智能指针或其他一些技术来实现。)

您认为还有哪些其他场景适合使用指针?

你会推荐今天学习指针吗?

【问题讨论】:

  • 这个问题完全没有问题。
  • 相关的stackoverflow.com/q/22146094 也可能涵盖这个问题。来自 Joseph Mansfield 的回答:“第二个 [问题] 是我们什么时候应该使用指针?”
  • @FoggyDay:学习与 C 共享的 C++ 部分是个好主意,但这绝对应该使用 C++ 编译器,而不是实际的 C。C 类型系统是一个不安全的笑话。它的一部分已被修复(不再隐含int),但部分仍然是灾难的邀请。有几个 C 功能很不错(命名为初始化程序和 restrict),但它们并没有开始弥补这些问题。
  • @FoggyDay C++11 不是“怪物”。学习汇编并不是成为一名优秀程序员的必备技能,大多数(90%+)人应该没有理由学习汇编。很多人没有,他们过得很好。

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


【解决方案1】:

我可以想象你有一个静态分配的数组并且你想使用一个原始指针在高性能代码中迭代它的情况。这仍然没有什么问题。

你的 #1 是真的。

您的 #2 可能不正确:如果您“使用强制转换”将第三方库拥有的原始指针转换为智能指针(暗示本地所有权),那么就出现了严重错误。

您的 #3 在技术上是正确的,但请尽可能避免这样做。

现在推荐的是使用指向你自己的动态分配内存的原始指针。也就是说,建议避免在没有智能指针的情况下使用new(推论是你不应该需要delete)。

【讨论】:

  • +1,尤其是最后一段。
  • 我认为#1 是基于意见的。听起来您必须在“风格的一致性”与“其他方法的优势”之间进行权衡。
  • “现在不推荐使用指向动态分配内存的原始指针。” 它是一种通用工具,因此专门的解决方案更适合任何特定的问题。但是,总是存在一些极端情况。例如。 coliru.stacked-crooked.com/a/a6c607514601b013
  • 我看不出“第三方库导出原始指针”如何等同于“第三方拥有对象”。另外,这就是std::unique_ptr 支持删除器的原因,因此可以将对象交回库进行清理。
  • 当你说第 3 条应该避免时,是因为有更好的方法来实现间接性,还是仅仅因为完成事情不需要间接性?
【解决方案2】:

每个人都反对原始指针,因为它太容易泄露它们。

但是您可以使用原始指针指向其他地方拥有的数据...只是不要new/delete 他们。为此使用std::unique_ptrstd::shared_ptr。或者对于哑 (POD) 内存缓冲区使用 std::vector<unsigned char>,不要自己使用 mallocfree

当您需要处理一些难以复制但已经存在于其他地方的对象的子选择时,请考虑使用std::vector<heavy_object*>。你需要这方面的指针。

您还需要函数中的指针用于可选参数,因为您希望能够传递nullptr

指向连续对象的指针也可以轻松迭代没有任何std::iterator 开销。只需++-- 即可。对于向量,我经常使用直接指针迭代而不是beginend

当您了解它们的工作原理时……您会非常需要它们,并且会正确使用它们。

【讨论】:

  • 谢谢,这对 nullptr 很有帮助。
  • 现在可选参数应该是std::optional<T>。为这些引入间接是一种反模式。我也很好奇你认为迭代器有什么“开销”。
  • @LightnessRacesinOrbit 我来自 C 背景。每当我可以并且数据连续时,我都会使用指针来遍历它。我认为这不会很快改变。我不知道开销,但我知道指针的开销最少……在我了解 C++ 标准中发生的所有事情之前,我将使用 C 风格使用接近金属。我知道这是让我这样做的原因是较小的编码员的不安全感,但它对我很有帮助。
  • 你的个人偏好是一回事,但是当你坦诚承认你不知道这是否属实时,断言迭代器有开销......这是不负责任的。
  • 关于向量:这可以很容易地用shared_ptr的向量替换,对吧?就这样我得到了正确的概念
【解决方案3】:

智能指针用于处理对象所有权问题,但并非所有指针都用于处理对象所有权问题。例如,如果您不打算将所有权传递给该函数(即您只希望函数处理指针所寻址的数据),则将原始指针传递给函数更有意义

【讨论】:

    【解决方案4】:

    恕我直言,原始指针仍然占有一席之地。

    C++11 为我们提供了管理原始指针生命周期的能力,这样我们就不必自己删除它们。

    使用原始指针没有任何问题,只要它们由智能指针/指针管理器在正确的范围或框架中管理,以确保它们的生命周期是正确的。如果这是真的,那么您永远不必删除原始指针,并且可以在保证其生命周期的范围/框架内安全地使用它们。

    我想说,如果可能的话,如果它的生命周期应该由给定的范围/框架控制,那么将指向新对象的原始指针存储在 std::unique_ptr 中。完成后,使用该范围框架内的原始指针。只是永远不要删除它;

    有时无法从单个范围或框架管理新对象的生命周期。在这种情况下,在每个独立需要管理新对象生命周期的范围/框架中使用std::shared_ptr。然后,在每个范围/框架内,没有理由不使用原始指针,就像它由 std::unique_ptr 管理时一样。

    因此通常没有理由招致智能指针的速度劣势,因为它们的优势之一在于管理新对象的生命周期,以确保原始指针的有效性和其自动销毁不再需要的对象。

    还有其他时候不适合使用原始指针。

    例如,当托管指针需要将“所有权”转移到另一个范围/框架时。那是当您需要负责管理新对象的生命周期的范围/框架来更改时。在这些情况下避免像瘟疫一样的原始指针!

    【讨论】:

      【解决方案5】:

      您认为还有哪些其他场景适合使用指针?

      使用原始指针的主要场景之一是当您有 非拥有 指针时。通常,引用可以工作,但您希望避免引用的约束(不可重新安装、不可复制)。在这些情况下,您可以使用 reference_wrapper 类型,但只使用原始指针会更简单。智能指针对所有权进行编码(谁创建和销毁对象),因此,如果没有要编码的所有权(因为它是隐含的),那么原始指针是可以的。

      为了清楚起见,我刚才解释的典型示例如下:

      • 临时可复制仿函数,需要指向它不拥有的某个对象的指针。
      • 数据结构中的内部交叉链接(例如,“反向指针”)。

      但重要的是要注意,这些东西通常不应该出现在接口中。通常,您可以在接口(例如库函数和类)中完全避免使用原始指针,并且只在内部真正使用它们,即在库端代码中,而不是在用户端代码中。换句话说,如果您需要使用原始指针,请将它们隐藏起来。

      原始指针有时也用于可选函数参数,如果您不想要该结果,可以在其中传入 nullptr

      应该避免并且通常可以避免的主要事情是在用户端代码中赤裸裸的new / delete 调用。一个典型的优秀现代 C++ 库(在 C++11 中更是如此)不会出现任何这种赤裸裸的new / delete,这是事实。

      原始指针本身并不是什么大问题,问题在于 (1) 手动内存管理,以及 (2) 所有权管理(如果使用原始指针而不是智能指针,则会出现问题)。

      你会推荐今天学习指针吗?

      当然你应该学习指针。它们对于理解编程和学习编写库端代码至关重要。原始指针仍然非常存在于许多库代码之类的内容中,即使您没有看到它们。

      【讨论】:

      • 我同意大部分答案(尤其是强调“仅对内存管理不利”),但是 1. std::shared_ptrstd::weak_ptr 不编码所有权。 2. 您可以将带有int& my_copy = othermy_copy(other) 的引用复制到对象中。 3. 我认为在很多情况下你可能会使用可重新安装的引用,你正在迭代,所以你可以使用for(int& x : thing) 来代替。也许在大多数情况下,需要可重新安装的引用是编写更高级别代码的机会。 4. 对于可选参数,有时可以使用默认值。
      【解决方案6】:

      在我脑海中使用原始指针的常见原因。

      1. 读取和解析二进制文件
      2. 直接与硬件交互
      3. 数组?
      4. 速度。 AFAIK 智能指针比原始指针慢,并且有相对安全的方法来使用原始指针。例如,请参阅 Chromium 的代码库或 WebKit 的。两者都使用各种跟踪内存,而没有智能指针的全部开销。当然也有限制。

      【讨论】:

      • 智能指针仅在执行更多操作时才比原始指针慢。 C++ 编译器非常擅长使 std::unique_ptr 编译为与原始指针和手动清理相同的内容...优点是您不会忘记对返回路径之一进行清理。
      • 我们说的是理论上的还是真实的? sizeof((SomeObject*)[100])sizeof(smart_ptr<SomeObject>[100]) 是什么? new (SomeObject*)[10000]new (smart_ptr<SomeObject>)[10000]的时差是多少?
      • sizeof 应该是一样的。分配应该与初始化原始指针块的值相同。但我正在将苹果与苹果进行比较;很容易开始将苹果与橙子进行比较,例如将std::shared_ptr 与使用原始指针且未正确计算引用计数的代码进行比较。
      • 为什么要占用更多内存? std::unique_ptr<T> 应该占用与 T* 一样多的内存。
      • “智能指针比原始指针慢” - 虽然这确实适用于某些智能指针类(例如你提到的shared_ptr),但它并不适用于所有这些类。您可能想阅读新的闪亮C++11 std::unique_ptr,它被设计为尽可能精简(本质上它在析构函数中调用delete,仅此而已 - 它不是引用计数指针)
      猜你喜欢
      • 2019-06-30
      • 2021-10-28
      • 2021-05-30
      • 2017-05-03
      • 2014-06-25
      • 1970-01-01
      • 1970-01-01
      • 2015-12-06
      • 1970-01-01
      相关资源
      最近更新 更多