C++11 引入的 nullptr 是一个专门用于表示空指针的关键字。
它的核心目的非常明确:彻底解决 C++98 中 NULL(本质是整数 0)带来的类型歧义问题,提供类型安全的空指针表示。
在 C++11 之前,我们用 NULL 或 0 表示空指针,但它们在编译器眼里其实是整数。nullptr 的出现,让"空指针"终于有了属于自己的身份。
下面我将从语法、核心优势、底层机制及使用限制四个方面为你详细介绍。
1. 基本语法
nullptr 的用法非常简单,它可以直接赋值给任何指针类型。
cpp
int* p1 = nullptr; // 初始化 int 指针
char* p2 = nullptr; // 初始化 char 指针
void* p3 = nullptr; // 初始化 void 指针
// 它的类型是 std::nullptr_t (定义在 <cstddef> 中)
std::nullptr_t np = nullptr;
2. 核心优势:为什么要用 nullptr?
🛡️ 消除函数重载的歧义(最核心痛点)
在 C++98 中,NULL 通常被定义为 0。当你重载了一个接受 int 和一个接受 指针 的函数时,传入 NULL 会发生什么?编译器会优先匹配 int 版本,这往往违背了程序员的初衷。
-
C++98(歧义/错误):
cppvoid func(int x) { cout << "调用了 int 版本" << endl; } void func(char* p) { cout << "调用了 指针版本" << endl; } func(NULL); // 输出:"调用了 int 版本" (因为 NULL 是 0) // 程序员本意是想传空指针,结果却调用了整数函数! -
C++11(明确):
cppfunc(nullptr); // 输出:"调用了 指针版本" // nullptr 只能转换为指针类型,编译器不再犹豫
🚫 类型安全:禁止转换为整数
nullptr 是一个纯指针概念,它不能隐式转换为整数类型。这防止了将空指针误用为数字的愚蠢错误。
cpp
int x = nullptr; // ❌ 编译错误!
int y = NULL; // ✅ 合法(但在现代 C++ 中不推荐),y 变为 0
🧩 模板推导更精准
在模板编程中,使用 NULL 会导致类型推导为 int,而 nullptr 能正确推导为指针类型或 std::nullptr_t。
cpp
template<typename T>
void printType(T t) { /* ... */ }
printType(NULL); // T 被推导为 int
printType(nullptr); // T 被推导为 std::nullptr_t 或 指针类型
3. 底层机制:std::nullptr_t
nullptr 不是一个宏,也不是整数,它是一个字面量 ,其类型是 std::nullptr_t。
- 定义 :
typedef decltype(nullptr) nullptr_t; - 转换规则 :
std::nullptr_t类型的值可以隐式转换为任何 原始指针类型(如int*,void*)和成员指针类型。 - 不转换 :它不能转换为整数类型。
4. 对比总结表
| 特性 | 0 |
NULL (C++98) |
nullptr (C++11) |
|---|---|---|---|
| 本质类型 | int |
int (通常是 0) |
std::nullptr_t |
| 指针语义 | 模糊(既是数字也是指针) | 模糊(宏定义,本质是数字) | 明确(纯指针) |
| 重载匹配 | 匹配 int 函数 |
匹配 int 函数 |
匹配 指针 函数 |
| 类型安全 | 低(可赋给 int) | 低(可赋给 int) | 高(不可赋给 int) |
| 推荐程度 | ❌ 绝不用于指针 | ❌ 不推荐 | ✅ 唯一推荐 |
5. 避坑指南
-
不要混用 :在 C++11 及以后的代码中,永远不要再用
NULL,全部替换为nullptr。 -
智能指针兼容 :
nullptr可以完美用于智能指针的初始化和重置。cppstd::shared_ptr<int> sp = nullptr; // 合法 sp = nullptr; // 合法,释放资源并置空 -
比较操作 :指针与
nullptr的比较是安全的。cppif (p == nullptr) { ... } // 推荐写法 if (!p) { ... } // 也可以,但语义不如前者明确
一句话总结:
nullptr 是 C++ 类型安全的一块重要拼图。它用专用的类型 解决了通用的数字 0 带来的混乱。记住口诀:"指针为空用 nullptr,告别 NULL 和 0!"