《Effective Modern C++》学习笔记 - Item 8: 倾向于使用 nullptr 而不是 0 或 NULL

  • 本Item内容较简单,养成良好习惯多用 nullptr 少用 0NULL 就好了。实际上,我认为可以说前者是从语言层面填补了C++98时代没有一个明确的空指针类型的空缺,因此在条件允许的情况下使用前者是完全优于后者的。以下是作者给出的几个更详细的原因。

  • 0 的本质是 int,只有当不得已时才会被转化为指针。NULL 同理(注:NULL 是一个宏定义,查看源码MSVC和GCC根据平台将其定义为了00LL。作为参考,其在C语言中的定义一般为(void *)0)。这里的重点是二者都不具有指针类型,这在函数重载中尤其会导致问题:
    在这里插入图片描述

更有意思的情况是,如果 NULL 被定义为 long,那么会调用哪个重载版本?转换成 intboolvoid* 看起来是差不多的同等好的选择。

  • 因此在C++98中,一般建议是不要给函数同时重载整型和指针型参数。
  • nullptr 的优势在于其没有一个整型类型。说实话它也不是指针类型,但你可以认为它是一个可以指向任何类型的指针。nullptr 的真实类型是 std::nullptr_t,通过一个循环定义,std::nullptr_t 的类型又被定义为 nullptrstd::nullptr_t 可以被隐式转换为任意裸指针类型。
  • 笔者注:MSVC将 std::nullptr_t 定义在 <stddef.h> 中,GCC则在 <c++config.h> 中,定义方式均为 typedef decltype(nullptr) nullptr_t。至于反过来的定义,由于 nullptr 是一个C++关键字,猜测应该不会出现在C++标准库实现的代码中,而是由编译器处理。也因为这种循环定义关系,当悬浮查看 std::nullptr_t 的类型时,会看到比较诡异的结果。
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  • 使用 nullptr 调用以上函数不会引起歧义,调用的是 void * 参数版本。
    在这里插入图片描述

  • 使用 nullptr 的另一好处是增强代码的清晰性,例如:

// 调用某个我们不了解细节的函数
auto result = findRecord( /* arguments */ );

if (result == 0) { 			// result是整型还是指针?
	...
}
if (result == nullptr) { 	// 很明确, result是指针
	...
}
  • 第三个好处与模板参数有关。作者举的原例子代码略长,但道理不复杂,这里进行一定简化:
template<typename FuncType, typename PtrType>
decltype(auto) callFuncOnPtr(FuncType func, PtrType ptr) {
    return func(ptr);
}

int f(void*) { return 0; }

int main()
{
    int a = callFuncOnPtr(f, 0);			// 编译错误
    int b = callFuncOnPtr(f, NULL);			// 同上
    int c = callFuncOnPtr(f, nullptr);		// ok
    return 0;
}
  • 如果我们想经过模板函数 callFuncOnPtr 调用参数为空指针的 func,很遗憾 0NULL 都行不通,因为在模板推导时 ptr 就会被认为是 int 型,而 func 需要的参数为 void *,导致编译错误(其实笔者这里没有完全理解:ptr 不能在 func 调用时再经过一次隐式转换吗?因为直接调用 f(0) 是没有问题的。或许对于模板只有一次定类型的机会?希望有高手能解答:)该场景下,使用 nullptr 是完全没有问题的。

总结

  1. 倾向于使用 nullptr 而不是 0 或 NULL。
  2. 避免同时重载整型和指针型函数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值