现在c++ 11有了许多新特性。一个有趣而令人困惑的(至少对我来说)是新的nullptr。
不需要讨厌的宏NULL了。
int* x = nullptr;
myclass* obj = nullptr;
不过,我还是不明白nullptr是如何工作的。例如,维基百科的一篇文章说:
c++ 11通过引入一个新的关键字作为区分空指针常量nullptr来纠正这一点。它的类型为nullptr_t,可隐式转换,可与任何指针类型或指针到成员类型相比较。它不能隐式转换,也不能与整型相比,bool类型除外。
它如何既是关键字又是类型的实例?
此外,你是否有另一个例子(除了维基百科的一个),其中nullptr优于好旧的0?
根据cppreference, nullptr是一个关键字,它:
表示指针字面量。它是std::nullptr_t类型的prvalue。
存在从nullptr到空指针值的隐式转换
任何指针类型和任何指向成员类型的指针。类似的转换
存在于任何空指针常量,其中包括类型的值
std::nullptr_t以及宏NULL。
因此nullptr是一个不同类型的值std::nullptr_t,而不是int。它隐式转换为任何指针类型的空指针值。这个神奇的事情发生在您的引擎盖下,您不必担心它的实现。然而,NULL是一个宏,它是一个实现定义的空指针常量。它通常是这样定义的:
#define NULL 0
也就是一个整数。
这是一个微妙但重要的区别,可以避免歧义。
例如:
int i = NULL; //OK
int i = nullptr; //error
int* p = NULL; //OK
int* p = nullptr; //OK
当你有两个像这样的函数重载:
void func(int x); //1)
void func(int* x); //2)
func(NULL)调用1),因为NULL是一个整数。
Func (nullptr)调用2),因为nullptr隐式转换为int*类型的指针。
另外,如果你看到这样的语句:
auto result = findRecord( /* arguments */ );
if (result == nullptr)
{
...
}
你不容易找到findRecord返回什么,你可以确定result必须是一个指针类型;Nullptr使其更具可读性。
在一个推断的背景下,事情的运作有点不同。如果你有一个这样的模板函数:
template<typename T>
void func(T *ptr)
{
...
}
你试着用nullptr调用它:
func(nullptr);
您将得到一个编译器错误,因为nullptr类型为nullptr_t。你必须显式地将nullptr转换为特定的指针类型,或者使用nullptr_t为func提供重载/专门化。
Advantages of using nulptr:
避免函数重载之间的模糊
使您能够进行模板专门化
更安全,直观和富有表现力的代码,例如if (ptr == nullptr)而不是if (ptr == 0)
它如何既是关键字又是类型的实例?
这并不奇怪。true和false都是关键字,作为字面量,它们有一个类型(bool)。Nullptr是一个std::nullptr_t类型的指针字面值,它是一个prvalue(不能使用&获取它的地址)。
4.10 about pointer conversion says that a prvalue of type std::nullptr_t is a null pointer constant, and that an integral null pointer constant can be converted to std::nullptr_t. The opposite direction is not allowed. This allows overloading a function for both pointers and integers, and passing nullptr to select the pointer version. Passing NULL or 0 would confusingly select the int version.
A cast of nullptr_t to an integral type needs a reinterpret_cast, and has the same semantics as a cast of (void*)0 to an integral type (mapping implementation defined). A reinterpret_cast cannot convert nullptr_t to any pointer type. Rely on the implicit conversion if possible or use static_cast.
The Standard requires that sizeof(nullptr_t) be sizeof(void*).
此外,你是否有另一个例子(除了维基百科的一个),其中nullptr优于好旧的0?
是的。这也是在我们的生产代码中出现的一个(简化的)真实例子。它之所以突出,是因为gcc能够在交叉编译到具有不同寄存器宽度的平台时发出警告(仍然不确定为什么只有在从x86_64交叉编译到x86时,警告警告:从NULL转换为非指针类型'int'):
考虑以下代码(c++ 03):
#include <iostream>
struct B {};
struct A
{
operator B*() {return 0;}
operator bool() {return true;}
};
int main()
{
A a;
B* pb = 0;
typedef void* null_ptr_t;
null_ptr_t null = 0;
std::cout << "(a == pb): " << (a == pb) << std::endl;
std::cout << "(a == 0): " << (a == 0) << std::endl; // no warning
std::cout << "(a == NULL): " << (a == NULL) << std::endl; // warns sometimes
std::cout << "(a == null): " << (a == null) << std::endl;
}
它产生如下输出:
(a == pb): 1
(a == 0): 0
(a == NULL): 0
(a == null): 1