必须用 nullptr 替换 NULL 和 0,因其类型为 std::nullptr_t,可隐式转为任意指针但不转整数,避免重载歧义;新代码统一使用,旧项目需甄别上下文迁移,并注意模板推导、auto 类型、智能指针初始化及兼容性问题。

用 nullptr 替换 NULL 和 0 是必须的
因为 NULL 本质是整数 0(或 0L),在函数重载场景下会意外匹配到 int 参数而非指针,引发静默错误。比如:void f(int) 和 void f(char*) 同时存在时,f(NULL) 会调用 f(int),而 f(nullptr) 只能匹配指针版本。
实操建议:
-
nullptr是类型安全的字面量,类型为std::nullptr_t,可隐式转为任意指针类型,但不能转为整数 - 所有新代码中,初始化指针、传参、返回空指针时,统一用
nullptr,不要写NULL或0 - 旧项目迁移时,全局搜索
NULL并逐个判断:若上下文明确是空指针用途(如int* p = NULL;),直接替换;若用于宏定义或位运算(如#define FLAG_NULL 0),保留原值
函数参数和模板推导中 nullptr 的行为要特别注意
模板函数里传 nullptr,编译器可能无法推导出目标指针类型,尤其当模板参数不是直接指针而是智能指针或容器时。
常见错误现象:template<typename t> void foo(T* p); foo(nullptr);</typename> 会编译失败 —— 编译器不知道 T 是什么。
立即学习“C++免费学习笔记(深入)”;
实操建议:
- 显式指定模板参数,如
foo<int>(nullptr)</int> - 改用非模板重载,或接受
std::nullptr_t的专用重载 - 对智能指针,优先用
std::shared_ptr<t>{}</t>或std::unique_ptr<t>{}</t>初始化,而不是std::shared_ptr<t>(nullptr)</t>—— 前者更清晰且避免不必要的构造函数调用
与 auto 和容器混用时容易误判类型
auto p = nullptr; 推导出的类型是 std::nullptr_t,不是任何指针类型。这在存入 std::vector 或做条件判断时可能出问题。
使用场景:想用 auto 声明一个“将来指向 int*”的变量,但写成 auto p = nullptr; 就锁死了类型。
实操建议:
- 避免单独用
auto p = nullptr;,应写成int* p = nullptr;或auto* p = static_cast<int>(nullptr);</int> - 容器若需存多种指针,别用
std::vector<:nullptr_t></:nullptr_t>—— 它只能存nullptr,毫无意义;改用std::vector<void></void>或更安全的std::variant<:monostate int char></:monostate> - 判断是否为空指针,始终用
if (p == nullptr),不要用if (!p)配合auto推导变量,以防p实际是整数或布尔
跨平台和老编译器兼容性陷阱
nullptr 是 C++11 引入的,VS2010、GCC 4.6+ 才支持。但真正容易踩坑的是“部分支持”:比如某些嵌入式工具链或旧版 Clang 允许 nullptr 字面量,却没实现完整的 std::nullptr_t 类型转换规则。
性能影响几乎为零 —— 它不产生运行时代价,只是编译期类型标记。
实操建议:
- 项目明确要求 C++11+ 时,放心用;否则加编译检查:
#if __cplusplus >= 201103L - 避免在宏里拼接
nullptr,如#define SAFE_DELETE(p) do { delete p; p = nullptr; } while(0)在 C++98 环境下会炸 - 第三方库头文件若未适配,可能把
nullptr当宏展开,导致语法错误;此时临时用0并加注释说明原因
最常被忽略的一点:nullptr 解决的是类型安全问题,不是空指针解引用问题。它不会防止你写 int* p = nullptr; *p = 42; —— 这种错误依然会在运行时报错或崩溃,编译器不拦。











