7.1.3 一些关于nullptr规则的讨论

nullptr这个名字看起来是比较古怪的。在C++98标准的时候,字符串NULL实际上已经得到了广泛的应用。而在C++11标准中,委员会采取了另起炉灶的方式,硬生生地添加了nullptr这个关键字,这让很多人表示不能理解。但另起炉灶而不是重用NULL的原因却是非常明显的。因为NULL已经是一个用途广泛的宏,且这个宏被不同的编译器实现为不同的解释,重用NULL会使得很多已有的C++程序不能通过C++11编译器的编译。因此为了保证最大的兼容性,委员会采用了新的名称nullptr,以避免和现有标识符的冲突。

此外在C++11标准中,nullptr类型数据所占用的内存空间大小跟void*相同的,即:


sizeof(nullptr_t)==sizeof(void*)


关于这一点也可能引起疑惑,即是否nullptr就是(void)0的一个别名。不过答案却是否定的,尽管两者看起来很相似,都可以被转换为任何类型的指针,但两者在语法层面有着不同的内涵。nullptr是一个编译时期的常量,它的名字是一个编译时期的关键字,能够为编译器所识别。而(void)0只是一个强制转换表达式,其返回的也是一个void*指针类型。

而且最为重要的是,在C++语言中,nullptr到任何指针的转换是隐式的,而(void*)0则必须经过类型转换后才能使用。我们可以看看代码清单7-5所示的例子。

代码清单7-5


int foo()

{

intpx=(void)0;//编译错误,不能隐式地将无类型指针转换为int*类型的指针

int*py=nullptr;

}

编译选项:g++ -std=c++11 7-1-5.cpp


可以看到,(void)0在使用上并不如nullptr方便。在nullptr出现之后,程序员大可以忘记(void)0,因为nullptr已经足够用了,而且也很好用。

注意 C语言标准中的void*指针是可以隐式转换为任意指针的,这一点跟C++是不同的。

此外,我们还注意到C++11标准有一条有趣的规定,nullptr_t对象的地址可以被用户使用(虽然看起来好像没什么实用价值)。但这条规则有一点例外,就是虽然nullptr也是一个nullptr_t的对象,C++11标准却规定用户不能获得nullptr的地址。其原因主要是因为nullptr被定义为一个右值常量,取其地址并没有意义。

不过C++11标准并没有禁止声明一个nullptr的右值引用,并打印其地址,因此我们在一些编译器上也做了个有趣的实验,对nullptr感兴趣的用户可以试运行一下代码清单7-6所示的这段的代码。

代码清单7-6


include <cstdio>

include <cstddef>

using namespace std;

int main(){

nullptr_t my_null;

printf("%x\n",&my_null);

//printf("%x",&nullptr);//根据C++11的标准设定,本句无法编译通过

printf("%d\n",my_null==nullptr);

const nullptr_t&&default_nullptr=nullptr;//default_nullptr是

nullptr的一个右值引用

printf("%x\n",&default_nullptr);

}

//编译选项:g++ -std=c++11 7-1-6.cpp


编译运行代码清单7-6,我们的实验机上的结果看起来是这样:


7498fca8

1

7498fcb0


当然,运行结果跟所用编译器以及平台都有关系。不过,对于普通用户而言,需要记得的仅仅是,不要对nullptr做取地址操作即可。