【问题标题】:Why is there no "NULL reference" in C++?为什么 C++ 中没有“NULL 引用”?
【发布时间】:2012-07-09 15:56:55
【问题描述】:

我正在阅读 C++ 常见问题解答 -“8.6 - When should I use references, and when should I use pointers?”,尤其是以下声明:

尽可能使用引用,必要时使用指针。

...

上述情况的例外是函数的参数或返回值需要一个“哨兵”引用——一个不引用对象的引用。这通常最好通过返回/获取一个指针,并赋予 NULL 指针特殊的意义(引用必须始终为对象别名,而不是取消引用的 NULL 指针)。

据我所见,对“哨兵”引用的需求确实经常是使用指针而不是引用的原因。我想知道的是:为什么 C++ 没有一个特殊的“NULL 值”用于引用?看起来它几乎不需要指针,这会解决很多问题。

那么为什么它不是语言规范的一部分呢?

编辑:

我不确定我的问题是否明确 - 我想我并没有在乱七八糟地询问 NULL 引用。大多数情况下,我在 C++ 中读到“引用就是对象”。而且,在大多数 OOP 语言中,对象可以为 NULL - Pascal、C#、Java、JavaScript、PHP 等。在所有这些中,您可以使用 someObject = null 或 someObject := nil。事实上,Pascal 也支持指针,但仍然允许对象为nil,因为它有它的用途。那么为什么 C++ 有某种特殊性并且没有 NULL 对象呢?这只是一个疏忽还是一个实际的决定?

【问题讨论】:

标签: c++ pointers reference null specifications


【解决方案1】:

因为引用带有指向一个永不改变的有效内存地址的语义;即取消引用它是安全/定义的,因此不需要 NULL 检查。不能按设计重新分配引用。

当 var 可以为 NULL 并且客户端代码必须处理这种情况时,您可以使用指针。当您可以保证有效/初始化的内存地址时,您可以使用引用。

使用指针的一个例子是作为类的成员来存储对某个实例的“引用”,该实例在类构造时可能未知或无法初始化。但是,成员引用必须在构造时初始化(通过初始化列表),并且它们的分配不能被推迟。

如果您允许空引用,那么除了语法之外,它与指针没有什么不同(需要进行相同的 NULL 检查。)

更新:

“而且,在大多数 OOP 语言中,对象可以是 NULL - Pascal、C#、Java、JavaScript、PHP 等。[...] 那么为什么 C++ 有点特殊并且没有 NULL 对象?是吗?只是一个疏忽还是一个实际的决定?”

我认为您对此有些困惑。 Java 和 C# 等可能给人的印象是“NULL 对象”,但这些对象引用或多或少像一个 C++ 指针,具有更简单的语法、GC 检测和异常抛出。在这些语言中,如果您对“空对象”进行操作,您将得到某种异常,例如 NullReferenceException (C#)。见鬼,在 Java 中它被称为 NullPointerException。

您必须先检查null,然后才能安全使用它们。有点像 C++ 指针(除了在大多数托管语言中,指针默认初始化为 NULL,而在 C++ 中,通常由您负责设置初始指针值(否则未定义/已经存在的任何内存)。

C++ 观点是关于有选择的,因此是冗长的:

  • 使用普通指针随心所欲,必要时检查 NULL。
  • 使用具有编译器强制有效性语义/约束的引用。
  • 滚动您自己的智能指针,用于记账并按照您希望的方式运行。
  • 如果需要,请使用 void 指针(小心!)来引用无类型的内存块。

【讨论】:

  • Nit:空指针和未初始化的指针是两个不同的东西。无法检测到未初始化的指针,至少不可靠,因为它可以指向任何地方,包括指向有效对象。
  • @hvd 感谢并修复。未初始化我的意思是 NULL。添加了有关 ctor 的部分,以说明指针的一种不同用途。
【解决方案2】:

请查看指针和引用之间的区别 - 虽然标准对引用的实现方式保持开放,但它们目前始终作为指针实现。

这意味着它们之间的主要区别是 a) 语义 b) 指针可以重新定位 c) 指针可以为空。

所以简短的回答是,这是故意的。当您作为程序员看到一个引用时,您应该知道 a) 该引用已填充 b) 它不会改变(并且 c) 您可以使用与对象相同的语义)。

标准是否允许空引用,您总是必须在使用引用之前检查空引用,这是不想要的。

编辑:

关于您的编辑,我想这里的混淆可能源于大多数简单的面向对象语言隐藏正在发生的事情这一事实。以 Java 为例,虽然看起来你有 NULL 对象,并且可以分配它们,但你真的不能 - 真正发生的是 Java 只有指针,并且可以为这些指针分配空值。由于在 Java 中实际上不可能直接拥有对象,因此它们取消了指针语义并将指针视为对象。 C++ 更强大 - 并且容易出错(Java 爱好者会说堆栈用户类实例不是必需的,并且在 Java 中不使用它们的决定是为了降低复杂性,并使 Java 更易于使用)。这也随之而来,因为 Java 没有对象,所以它没有引用。然而,真正没有帮助的是,Java 将 C++ 人所说的指针的值传递称为引用传递。

【讨论】:

  • 我已经更新了我的问题。您如何解释几乎所有 OO 语言都允许 null 引用,但 C++ 不允许?在所有这些其他语言中,您并不总是需要检查 NULL,它完全取决于上下文。
  • 我不知道那么多面向对象的语言,但对于我知道的那些我不同意。 Java 甚至没有引用,Java 只有指针(例如可以为空,可以重新安装),尽管语法不同。 Java 没有引用的原因之一是因为 Java 也没有堆栈变量(缺少内置类型) - 一切都是堆,每个变量都是指针,因此可以为 null,没有堆栈内存,不需要参考。 vba 也是如此。这大约是我所知道的所有面向对象语言的数量。
  • 考虑例如C++ 中引用的一个典型用例:您有一些大堆栈分配的变量,并希望通过引用而不是值来传递它们。在这里您需要使用真正的 C++ 引用,并且这样做是正确的,因为它们必须是非 null 并且不会更改。在Java中,你不能有堆栈变量,所以调用环境总是只有指针,所以你不需要构造来通过引用传递。经常会出现混淆——Java 人所说的“按引用传递”,C++ 人真正应该称之为“按指针传递”。
【解决方案3】:

C++ 引用不是大多数其他语言使用该术语的意义上的引用。它更像是一个别名。使用 C++ 引用并不涉及以与取消引用指针相同的方式“取消引用”它;它实际上是分配给它的对象。

实际上并没有空对象实例这样的东西,所以你不能创建对这样的东西的C++引用。在其他语言中,空引用等同于 C++ 空指针。它实际上不包含任何内容。

现在,关于您的其他想法: 1. 在我看来,拥有不可为空的引用是一件 好 的事情;这意味着您可以获得通过引用传递的所有好处,而无需在所有地方检查空值。 2. 可空引用不能替代指针...如果您需要在低级别进行内存或 IO 工作,您将需要对内存和内存映射设备的原始访问。您可以看看 C#(或 C++/CLR),正是出于这个原因,它具有托管引用和非托管指针。

【讨论】:

    【解决方案4】:

    定义引用应该与另一个变量或对象相关联。所以一个空的或空的引用违反了它的存在目的。

    从技术上讲,这意味着您从空引用开始,然后将其分配给某个变量,或者稍后重新分配给另一个变量。这根本不是要创建引用的目的。

    引用不能复制的指针还有其他几种用途。例如,使用指针引用具有少量内存的大量复杂数据(字节序列),并通过仅花费 8 个字节左右的指针来根据需要频繁地传递它。你不能在不浪费相同内存的情况下使用引用来做到这一点。

    引用总是与类型相关联,而指针则不是这种情况。

    【讨论】:

    • 前 2 段是正确的。最后一个没有意义。
    • 我心里肯定有一个用例,在这里可能会或可能不会正确阐述。但上面的评论不加赘述,甚至没有意见,仅仅“没有意义”就更没用了,没有分量
    【解决方案5】:

    引用必须引用某些东西,因此,空引用不可用。
    C 没有引用,在这种语言中我们只能使用指针,因此,这是与 C 语言的兼容性

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-09
      • 2021-09-29
      • 2020-04-25
      • 1970-01-01
      • 1970-01-01
      • 2017-06-03
      相关资源
      最近更新 更多