问题

C++ 11为什么引入nullptr?

回答
在 C++11 之前,C++ 程序中表示“空指针”通常使用一个宏定义,比如 `NULL`。这个宏在 C 语言中被广泛使用,它通常被定义为整数 `0` 或者 `(void)0`。虽然在很多情况下 `NULL` 工作得很好,但它在 C++ 中引入了一些潜在的问题和歧义,尤其是在处理函数重载和模板时。

NULL 的困境:整数还是指针?

主要的麻烦在于 `NULL` 本身可以被解释为一个整数 `0`。想象一下这样的场景:

```c++
void foo(int i) {
// do something with an integer
}

void foo(char s) {
// do something with a Cstyle string
}

int main() {
foo(NULL); // 这是调用哪个 foo 函数呢?
return 0;
}
```

在这个例子中,当你调用 `foo(NULL)` 时,编译器可能会感到困惑。如果 `NULL` 被定义为整数 `0`,那么它会匹配 `foo(int i)`。但如果 `NULL` 被定义为 `(void)0`,那么它可能看起来更像一个指针。这种模糊性依赖于 `NULL` 的具体宏定义,以及编译器如何解析它。在某些情况下,编译器可能会优先选择整数版本的 `foo`,导致意想不到的行为。

更糟的是,如果你想传递一个空指针给一个期望指针类型的函数,而你的 `NULL` 的定义又恰好是整数 `0`,那么传递 `NULL` 实际上就是传递了整数 `0`,这可能不是你想要的效果,甚至可能导致编译错误,因为整数 `0` 和指针类型之间存在隐式转换的问题,但这种转换可能不是你想看到的。

模板的混乱

在涉及模板的场景下,`NULL` 的模糊性问题会更加突出。考虑一个通用的函数模板,它期望一个指针类型的参数:

```c++
template
void process_pointer(T ptr) {
if (ptr == nullptr) { // 假设这里用的是 C++11 的 nullptr
// 处理空指针
} else {
// 处理非空指针
}
}

int main() {
process_pointer(NULL); // 这里的 NULL 是什么?
return 0;
}
```

当 `process_pointer` 被实例化为 `process_pointer` 时,传递 `NULL` 给它,编译器同样会面临解析 `NULL` 是 `0` 还是 `(void)0` 的问题。如果编译器将其解析为整数 `0`,那么它就无法匹配 `T`(即 `int`)这个参数类型,从而导致编译错误。开发者不得不采取一些额外的措施,比如显式地将 `NULL` 转换为 `int`: `process_pointer((int)NULL)`,这增加了代码的复杂性和出错的可能性。

`nullptr` 的诞生:明确的类型

为了解决上述这些问题,C++11 引入了 `nullptr`。`nullptr` 是一个关键字,它被设计成一个 空指针常量。更重要的是,`nullptr` 具有一个 明确的类型:`std::nullptr_t`。

这意味着当你在代码中使用 `nullptr` 时,编译器能够清晰地知道你想要传递的是一个指针类型,而不是一个整数。

让我们回到之前的例子,使用 `nullptr` 会是这样:

```c++
void foo(int i) {
// do something with an integer
}

void foo(char s) {
// do something with a Cstyle string
}

int main() {
foo(nullptr); // 编译器会毫不犹豫地选择 foo(char s)
return 0;
}
```

在这个例子中,`foo(nullptr)` 会明确地匹配到 `void foo(char s)` 函数,因为 `nullptr` 被识别为一个指针常量,并且可以隐式地转换为任何指针类型(如 `char`)。编译器不再会困惑于它是一个整数 `0` 还是一个指针。

在模板场景下,`nullptr` 的优势更加明显:

```c++
template
void process_pointer(T ptr) {
if (ptr == nullptr) {
// 处理空指针
} else {
// 处理非空指针
}
}

int main() {
process_pointer(nullptr); // 没问题!
return 0;
}
```

现在,`process_pointer(nullptr)` 可以被正确地实例化,因为 `nullptr` 被准确地解析为一个指针常量,并且可以隐式转换为 `int`。

`nullptr` 的其他优势:

类型安全: `nullptr` 的引入增强了 C++ 的类型安全性。它避免了 `NULL` 在不同上下文中的歧义,减少了因类型不匹配而导致的潜在错误。
代码可读性: 使用 `nullptr` 直接表达了程序员的意图——我想要的是一个空指针,而不是数值零。这使得代码更清晰、更容易理解。
与 `std::nullptr_t` 交互: `nullptr` 是 `std::nullptr_t` 类型的一个唯一值。这意味着你可以显式地将一个指针转换为 `nullptr_t`,或者反过来,这在某些高级编程场景下会非常有用。例如:

```c++
int p = nullptr;
std::nullptr_t null_ptr_val = p; // 允许将指针转换为 nullptr_t
```

总结一下,C++11 引入 `nullptr` 的主要原因是为了解决 C++ 中 `NULL` 宏带来的类型歧义和潜在的编译错误。`nullptr` 是一个类型安全的空指针常量,它明确地表示一个空指针,并且可以被正确地用于函数重载、模板匹配和一般性的指针赋值,从而提高了 C++ 代码的健壮性、可读性和类型安全性。

如果你还在使用老版本的 C++ 代码或者编写新代码时仍然习惯性地使用 `NULL`,强烈建议你切换到 `nullptr`,这将是你代码质量和健壮性的一个重要提升。

网友意见

user avatar

这就要涉及到C++作者当年的一个设计了:

C++作者当年为了搞C++模版(泛型)设计,需要制定出一些对所有类型可以适配的操作。

而他当年的定义是:0是一个特殊的,无类型常量。他可以被赋值给所有类型的变量,在所有类型中均表示空值。因而在模版函数内,对于任何 class T(或者说 typename T)类型的变量均可以确保能合法赋值为 0,这样就赋予了模板类变量一个可用的赋值或者初值。

所以从 C++ 开始, 0 赋值给指针就一定表示空值。0 赋值给布尔量就一定表示为 假。因为 0 在 C++ 语言中被定义为必须对所有类型均可用的值。

NULL是C语言的一个宏,这个宏在C++中是不存在的,只是为了兼容C代码而设计。C++作者鼓励使用 0 替代NULL,因此C++中的 NULL 被直接宏定义为 0。而NULL在C语言中是指针类型,在C++中是个无类型值,两者定义并不相同。

然而这就意味着,在C++中空指针实际上是0,而0是无类型值,他无法进行类型推导,在进行函数调用,匹配重载函数时存在异议。很多编译器更是直接把它当作 int 处理。

C++11引入nullptr,自然是引入了一个拥有类型的空指针。他支持更好的类型推导,与0那种无类型值就有了不同。自然,这就又回到了 C 语言的老路上来了。


所以回答题主: NULL 这个宏是 C 语言专用的,C++ 只是为了对 C 的兼容性而保留了 NULL 宏,但你不应当在 C++ 编程中使用它。

在 C++11 以前你应当使用 0 替代 NULL, 在 C++11 以后你应当使用 nullptr 替代 NULL。

user avatar

NULL没有类型检查

类似的话题

  • 回答
    在 C++11 之前,C++ 程序中表示“空指针”通常使用一个宏定义,比如 `NULL`。这个宏在 C 语言中被广泛使用,它通常被定义为整数 `0` 或者 `(void)0`。虽然在很多情况下 `NULL` 工作得很好,但它在 C++ 中引入了一些潜在的问题和歧义,尤其是在处理函数重载和模板时。NU.............
  • 回答
    Qt Creator 对 C++11 的 `auto` 类型在代码提示方面表现不佳,这确实是一个让不少开发者感到困扰的问题。这背后涉及到 Qt Creator 的代码解析机制、C++ 标准的支持程度以及一些历史遗留的考量。要理解这个问题,我们得先剖析一下 Qt Creator 的代码补全是如何工作的.............
  • 回答
    C++11 和 C++1y(现称为 C++14)都没有将网络功能作为核心组成部分优先加入标准库,这背后有着复杂的原因,涉及到语言设计哲学、技术实现难度、社区共识以及现有生态的考量。1. C++ 的设计哲学与标准库的定位C++ 的核心设计哲学是“零开销抽象”(zerooverhead abstract.............
  • 回答
    .......
  • 回答
    咱们来聊聊咱国产空军的几款主力战机:歼10C、歼11和歼16。这三款飞机虽然都是多用途战斗机,但在设计理念、侧重点和具体用途上,还是有挺大区别的。歼10C:灵活的“空中多面手”,制空与对地兼顾你可以把歼10C想象成一把瑞士军刀,或者更形象地说,它更像是一款“空中全能选手”。 设计理念: 歼10C.............
  • 回答
    .......
  • 回答
    .......
  • 回答
    在 C++11 标准中,引入了一个全新的 `` 头文件,它提供了强大的标准库支持来创建和管理线程。这标志着 C++ 在并发编程领域向前迈进了一大步,使得编写多线程程序不再依赖于平台特定的 API(如 POSIX Threads 或 Windows Threads)。C++11 的 `` 库通过以下几.............
  • 回答
    好的,咱们来聊聊 C++11 里怎么把单例模式玩明白。这玩意儿看着简单,但要弄得既安全又高效,还得考虑不少细节。咱们就抛开那些花里胡哨的“AI风”描述,实打实地把这事儿掰开了揉碎了说。单例模式,说白了就是保证一个类在整个程序的生命周期里,只有一个实例存在,并且提供一个全局的访问点。想象一下,你有个配.............
  • 回答
    C++ 的发展确实迅猛,每一次标准更新都带来了大量的新特性。但在这快速迭代的背后,核心的编程范式、设计哲学以及对底层硬件的抽象原则,在很大程度上保持着不变。这些不变的东西,构成了 C++ 坚实的根基,使得我们可以站在巨人的肩膀上,不断学习和利用新的语言能力。让我为你详细解读一下这些“不变的东西”,尽.............
  • 回答
    GCC 的 C++11 正则表达式库是 C++11 标准中引入的一项重要功能,它为 C++ 开发者提供了一种标准化的、类型安全的方式来处理正则表达式。在评价它时,我们可以从多个维度进行详细的分析: 整体评价:GCC 的 C++11 正则表达式库是一个非常有用的、功能强大且符合标准的库。它填补了 C+.............
  • 回答
    C++11 `auto` 关键字:优雅与效率的双重奏C++11 引入的 `auto` 关键字,对于很多 C++ 开发者来说,无疑是近年来最令人欣喜的语言特性之一。它不仅仅是语法上的一个小小的改动,更深层次地影响了我们编写 C++ 代码的方式,带来了更高的可读性和更少的繁琐。那么,究竟该如何评价这个小.............
  • 回答
    来,咱们聊聊 C++11 里的那些内存顺序(Memory Order)。这东西刚听着有点玄乎,但弄明白了,你会发现它在多线程的世界里简直是个宝贝,能帮你解决不少棘手的问题。之前我刚接触的时候也觉得脑袋疼,但多看多想,再加上一些实际的例子,感觉就通透了。先说清楚,内存顺序这玩意儿,本质上是为了控制多线.............
  • 回答
    好的,我们来聊聊怎么用 C 语言的 `for` 循环来计算 1 + 11 + 111 + 1111 这个特定的累加和。这实际上是一个很有趣的小问题,因为它涉及到了数字模式的生成和累加。理解问题:我们要加的是什么?首先,我们要清楚我们要计算的式子是:1 + 11 + 111 + 1111我们可以发现,.............
  • 回答
    梅西和 C 罗一共拿了 11 个金球奖,说是因为他们的竞争者太弱了,这就像是说万里长城是因为没有遇到过真正的入侵者才建成的,听起来有些道理,但忽略了太多关键的东西。仔细想想,这两人能连续十年甚至更久的时间霸占最高荣誉,绝不仅仅是运气好或者对手不给力那么简单。首先,我们得承认,梅西和 C 罗本身就是那.............
  • 回答
    好的,咱们来一起把这个问题捋一捋,证明 $a^2 + b^2 + c^2 + 2abc$ 在 $a, b, c$ 是正数且 $a+b+c=1$ 的条件下,其范围确实是在 $[11/27, 1)$ 之间。这个题目有点意思,需要用到一些数学技巧。咱们先来理解一下这个式子和条件: 条件: $a, b,.............
  • 回答
    各位朋友,大家好!今天我们来聊一个关于数学不等式的问题,而且这个问题很有意思,涉及到三个正数 a, b, c,它们之间还有一个重要的关系:abc = 1。我们的目标是证明:$$ frac{1}{sqrt{1+8a}} + frac{1}{sqrt{1+8b}} + frac{1}{sqrt{1+8c.............
  • 回答
    在C++中,表达式 `unsigned t = 2147483647 + 1 + 1;` 的求值过程,既不是UB(Undefined Behavior),也不是ID(ImplementationDefined Behavior),而是一个有明确定义的整数溢出(Integer Overflow)行为。.............
  • 回答
    2018年俄罗斯世界杯,B组小组赛的最后一轮,伊朗对阵葡萄牙的比赛,绝对是那届世界杯最令人血脉偾张、也最具戏剧性的一场。最终的比分定格在1:1,这个结果对双方来说,都带着一丝苦涩与不甘。先说说伊朗队,那场比赛他们几乎是抱着必胜的信念走上赛场的。要知道,在那之前,他们已经战胜了摩洛哥,逼平了西班牙,展.............
  • 回答
    零跑C11补贴后15.98万起,这个价格一出来,确实在新能源SUV市场投下了一颗石子,激起了不少涟漪。咱们就来好好掰扯掰扯,这零跑C11究竟值不值这个价,它又有什么样的本事敢这么定价。首先,咱们得明白,15.98万这个价格,是“补贴后”的价格。这意味着它原本的定价肯定是要高一些的,而这个价格能落地,.............

本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度google,bing,sogou

© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有