转向现代C++——优先选用nullptr而不是0和NULL

文章目录

    • [优先选用 nullptr,而非 0 或 NULL](#优先选用 nullptr,而非 0 或 NULL)

优先选用 nullptr,而非 0 或 NULL

优先选用 nullptr,而非 0NULL,其核心原因可以总结为一句话:0和NULL的本质是整型,而nullptr 的本质才是真正的指针。**

在模板编程、函数重载以及类型推导中,把整型当指针用会引发各种让人抓狂的二义性和编译错误。

由于 0intNULL 在大多数编译器下被定义为 0 或者 0L(长整型),它们在重载决议中会被优先识别为数值,而不是指针。

cpp 复制代码
#include <iostream>

// 重载函数 1:接收整型
void f(int x) {
    std::cout << "调用了 f(int)\n";
}

// 重载函数 2:接收空指针
void f(void* p) {
    std::cout << "调用了 f(void*)\n";
}

int main() {
    // 1. 传入数值 0
    f(0);       // 输出:调用了 f(int) 
                // 原因:0 是字面量整型,完美匹配 f(int)

    // 2. 传入宏 NULL
    f(NULL);    // 输出:调用了 f(int) 
                // 原因:NULL 实际上就是 0 或 0L,依然被当作整型处理

    // 3. 用户的本意是想传空指针,但上面两个都调用错了!
    
    // 4. 传入真正代表指针的 nullptr
    f(nullptr); // 输出:调用了 f(void*)
                // 原因:nullptr 的类型是 std::nullptr_t,它能隐式转换为任何指针类型,
                // 但绝对不会转换为整型!
}

当模板进行自动类型推导时,0 和 NULL 的整型身份会彻底暴露,导致原本合法的代码直接编译报错

cpp 复制代码
//假设我们要写一个通用的"锁管理执行器"模板,它负责:上锁 -> 执行函数 -> 解锁。
#include <mutex>
#include <memory>
#include <utility>

std::mutex mtx;

// 模拟三个不同参数的底层函数
void g1(int* p) {}
void g2(std::shared_ptr<int> p) {}
void g3(int x) {}

// ==================== 泛型执行器模板 ====================
template<typename FuncType, typename PtrType>
void logAndExecute(FuncType func, PtrType param) {
    std::lock_guard<std::mutex> lock(mtx); // 上锁
    func(param);                           // 执行函数
}

int main() {
    // ---- 场景 A:不使用模板,直接调用(由于隐式转换,强行能跑) ----
    g1(0);       // 勉强通过,0 可以隐式转换为 int*
    g1(NULL);    // 勉强通过
    g1(nullptr); // 正确通过


    // ---- 场景 B:使用模板(灾难降临) ----
    
    // 1. 尝试传入 nullptr
    logAndExecute(g1, nullptr); // 成功!
    // 编译器推导:PtrType 为 std::nullptr_t。
    // 在模板内部执行:g1(nullptr),std::nullptr_t 隐式转换为 int*,完美!

    // 2. 尝试传入 0
    // logAndExecute(g1, 0); 
    /* 
       【编译报错!】
       编译器的内心活动:
       1. 传入了数值 0,因此我推导 PtrType 的实际类型为 int。
       2. 实例化模板内部代码:func(param) -> 变成了 g1(int x)。
       3. 糟糕!g1 接收的参数是 int* 类型的指针,而你传给它一个 int 类型的变量(param)!
       4. 整型变量是绝对不能隐式转换成指针的(字面量 0 可以,但装进变量里就不行了)。
       5. 报错:无法将 'int' 转换为 'int*'
    */

    // 3. 尝试传入 NULL
    // logAndExecute(g2, NULL);
    /* 
       【编译报错!】
       同样的道理,NULL 被推导为 long 或 int。
       在模板内部试图把一个整型变量传给 std::shared_ptr<int>,直接引发语法错误。
    */
}

除了编译期的绝对安全,nullptr 还能提升代码的清晰性。

cpp 复制代码
// 假设你正在审查别人的代码,看到了这一行:
auto result = findRecord( /* 参数 */ );

//不清楚返回的是整数类型还是指针类型
if (result == 0) {
    // ...
}

//返回的肯定是指针类型
if (result == nullptr) {
    // ...
}
相关推荐
我星期八休息1 小时前
Linux系统编程—基础IO
linux·运维·服务器·c语言·c++·人工智能·算法
萌新小码农‍2 小时前
python装饰器
开发语言·前端·python
KK溜了溜了2 小时前
Python从入门到精通
服务器·开发语言·python
故事和你912 小时前
洛谷-【图论2-1】树5
开发语言·数据结构·c++·算法·动态规划·图论
threelab2 小时前
Three.js 初中数学函数可视化 | 三维可视化 / AI 提示词
开发语言·前端·javascript·人工智能·3d·着色器
xiaoshuaishuai83 小时前
C# CDN加速与离线包优化PowerSetting慢问题
开发语言·windows·spring·c#
paeamecium3 小时前
【PAT甲级真题】- String Subtraction (20)
数据结构·c++·算法·pat考试·pat
凉辰3 小时前
解决 H5 键盘遮挡与页面上推
开发语言·javascript·计算机外设
计算机安禾3 小时前
【c++面向对象编程】第25篇:仿函数(函数对象):重载operator()
开发语言·c++·算法