比起0和NULL,难道你不更偏爱nullptr吗?

摘要:本文翻译自modern effective C++,由于水平有限,故无法保证翻译完全正确,欢迎指出错误。谢谢! 博客已经迁移到 "这里啦" 先让我们看一
本文翻译自modern effective C++,由于水平有限,故无法保证翻译完全正确,欢迎指出错误。谢谢! 博客已经迁移到这里啦 先让我们看一些概念:字面上的0是一个int,不是一个指针。如果C++发现0在上下文中只能被用作指针,它会勉强把0解释为一个null指针,但这只是一个应变的方案。C++的主要规则还是把0视为int,而不是一个指针。 实际上,NULL也是这样的。NULL的情况,在细节方面有一些不确定性,因为C++标准允许它的实现不管是不是int只需要给出一个数值类型(比如,long)。虽然这和0不一样,但是事实上没有关系,因为这里的问题不是NULL的具体类型,而是0和NULL都不是指针类型。 在C++98中,这个问题造成的最主要的影响就是指针和数值类型的重载会导致意外情况。传入一个0或者NULL给这样的重载,则永远不会调用指针版本的重载: void f(int); //f的三个重载 void f(bool); void f(void*); f(0); //调用f(int), 而不是f(void*) f(NULL); //可能不能通过编译,但是通常会调用f(int), //永远不会调用f(void*) 关于f(NULL)行为的不确定性反映了:关于NULL类型的实现(编译器)拥有自由的权利。比如,如果NULL被定义为0L(也就是long类型的0),那么对函数的调用引起歧义,因为把long转换为int,bool,void*都是同样可行的。关于调用f的一个有趣的事情是源代码表面上的意义(我使用NULL---null指针,调用f)和它实际的意义(我使用某种整形---不是一个指针,调用f)之间的矛盾。这种反直觉的行为导致了C++98程序员指导方针让我们避免重载指针类型和整形类型。这个方针在C++11中仍然有效,因为,就算有这条Item的建议,尽管nullptr是更好的选择,有些开发者还是会继续使用0和NULL。 nullptr的优点不只是它不是一个整形类型。老实说,它也不是一个指针类型,但是你能把它想成是一个指向所有类型的指针。nullptr的实际类型是std::nullptr_t,在一个完美的循环定义(circular definition)中,std::nullptr_t被定义为nullptr的类型。std::nullptr_t类型能隐式转换到所有的原始(raw)指针类型,就是这个原因,让nullptr表现得像它是所有类型的指针。 用nullptr调用f的重载函数,实际会调用void*版本的函数(也就是指针版本的重载),因为nullptr不能被视为任何数值类型: f(nullptr); //调用f(void*)版本的重载函数 因此使用nullptr来代替0和NULL避免了意外的重载解析,但是这不是他唯一的优点。它还能提升代码的可读性,尤其是在涉及auto变量时。举个例子,假设你遇到下面的代码: auto result = findRecord(/* arguments*/); if(result == 0){ ... } 如果你碰巧不知道(或者很难找出)findRecord返回的类型,这里对于result是指针类型还是整形类型就不明确了。总之,0(用来和result进行比较)可以是两个中的任意一个。但是,如果你看到下面的代码: auto result = findRecord(/* arguments*/); if(result == nullptr){ ... } 这里将没有歧义:result肯定是一个指针类型。 当template涉及后,nullptr将变得更加耀眼。假设你有一些函数,它们只能在适当的互斥量被锁住之后才能调用。
阅读全文