17370845950

c++中nullptr和NULL的区别_c++11空指针规范【基础】
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,导致模板推导失败(如 tem

    plate void g(T*)
    接收 NULL 时推导为 g
  • nullptr 在模板中能正确推导为指针类型,且支持 auto 推导: auto p = nullptr;p 类型是 std::nullptr_t
  • 某些旧头文件中 NULL 定义依赖于 __cplusplus 宏,跨平台或混合 C/C++ 编译时行为不一致

nullptr 的类型和隐式转换规则

nullptr 不是关键字而是字面量,其类型是唯一的 std::nullptr_t,定义在 中。它只能隐式转换为指针类型(包括成员指针),不能转为 boolint 或用户自定义类型(除非你显式提供接受 std::nullptr_t 的构造函数)。

  • 允许:int* p = nullptr;void (A::*mp)() = nullptr;if (p == nullptr)
  • 禁止:int x = nullptr;(编译错误)、bool b = nullptr;(需写成 bool b = (p != nullptr);
  • 注意:nullptr0 在比较中等价,但语义完全不同;nullptr == 0 合法,但这是靠指针与整数的比较规则,不推荐这样写

在 C++11 及以后项目中如何安全迁移

已有代码大量使用 NULL 时,不能简单全局替换为 nullptr,尤其当涉及宏、函数参数或模板特化时。

  • 优先替换函数调用中的 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)),且文档说 “dataNULL 表示忽略”,那传 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 就是把这类隐患从运行时提前到编译期暴露出来。