NULL 不类型安全,易导致重载二义、模板推导失败;nullptr 是 C++11 引入的 std::nullptr_t 字面量,只转指针类型,保障类型安全与编译期检查。

为什么不能用 NULL 替代 nullptr
NULL 在 C++98/03 中本质是整数 0 或宏定义(如 #define NULL 0 或 #define NULL ((void*)0)),它不是类型安全的。当函数重载存在 void f(int) 和 void f(char*) 时,f(NULL) 会调用 f(int),而非预期的指针版本——这是最典型的二义性问题。
而 nullptr 是 C++11 引入的字面量,类型为 std::nullptr_t,可隐式转换为任意指针类型,但不会转换为整数类型。因此 f(nullptr) 必定匹配指针重载版本。
-
NULL可能被编译器当作int,导致模板推导失败(如template接收void g(T*) NULL时推导为g) -
nullptr在模板中能正确推导为指针类型,且支持auto推导:auto p = nullptr;→p类型是std::nullptr_t - 某些旧头文件中
NULL定义依赖于__cplusplus宏,跨平台或混合 C/C++ 编译时行为不一致
nullptr 的类型和隐式转换规则
nullptr 不是关键字而是字面量,其类型是唯一的 std::nullptr_t,定义在 中。它只能隐式转换为指针类型(包括成员指针),不能转为 bool、int 或用户自定义类型(除非你显式提供接受 std::nullptr_t 的构造函数)。
- 允许:
int* p = nullptr;、void (A::*mp)() = nullptr;、if (p == nullptr) - 禁止:
int x = nullptr;(编译错误)、bool b = nullptr;(需写成bool b = (p != nullptr);) - 注意:
nullptr和0在比较中等价,但语义完全不同;nullptr == 0合法,但这是靠指针与整数的比较规则,不推荐这样写
在 C++11 及以后项目中如何安全迁移
已有代码大量使用 NULL 时,不能简单全局替换为 nullptr,尤其当涉及宏、函数参数或模板特化时。
立即学习“C++免费学习笔记(深入)”;
- 优先替换函数调用中的
NULL:如func(NULL)→func(nullptr),这是最安全、收益最大的改动 - 避免在宏定义里直接用
nullptr(如#define SAFE_DELETE(p) do { delete p; p = nullptr; } while(0)是 OK 的;但若宏用于 C 头文件则仍需保留NULL) - 模板代码中若曾用
NULL做默认参数(如template),应改为void foo(T* p = NULL) = nullptr,否则实例化时可能因T非指针而编译失败 - 第三方库接口若明确要求
const char*或void*,传nullptr没问题;但若接口是 C 风格(如some_c_api(int flag, void* data)),且文档说 “data为NULL表示忽略”,那传nullptr依然合法——因为nullptr可转为任意指针类型
容易被忽略的边界情况
真正出问题的地方往往不在主干逻辑,而在细节交互处。
- 使用
std::function绑定空函数对象时:std::function合法,但f = nullptr; f()会抛std::bad_function_call;而f = NULL可能因类型不匹配编译失败 - 在
constexpr上下文中:constexpr int* p = nullptr;合法;但constexpr int* q = NULL;在某些老编译器上可能不被接受(因NULL展开为非字面量表达式) - 类成员初始化列表中:
MyClass() : ptr(NULL) {}应改为: ptr(nullptr),否则若ptr是智能指针(如std::unique_ptr),用NULL初始化可能触发隐式int构造,造成未定义行为
最麻烦的不是语法错误,而是那些“看起来能过编译、运行也不报错、但逻辑悄悄走歪”的情况——比如重载选错、模板实例化异常、或者跨模块 ABI 不一致。用 nullptr 就是把这类隐患从运行时提前到编译期暴露出来。











