现在c++ 11有了许多新特性。一个有趣而令人困惑的(至少对我来说)是新的nullptr。

不需要讨厌的宏NULL了。

int* x = nullptr;
myclass* obj = nullptr;

不过,我还是不明白nullptr是如何工作的。例如,维基百科的一篇文章说:

c++ 11通过引入一个新的关键字作为区分空指针常量nullptr来纠正这一点。它的类型为nullptr_t,可隐式转换,可与任何指针类型或指针到成员类型相比较。它不能隐式转换,也不能与整型相比,bool类型除外。

它如何既是关键字又是类型的实例?

此外,你是否有另一个例子(除了维基百科的一个),其中nullptr优于好旧的0?


当前回答

假设你有一个重载的函数(f),它同时接受int和char*。在c++ 11之前,如果你想用空指针调用它,并且你使用了null(即值0),那么你会调用int重载的指针:

void f(int);
void f(char*);

void g() 
{
  f(0); // Calls f(int).
  f(NULL); // Equals to f(0). Calls f(int).
}

这可能不是你想要的。c++ 11用nullptr解决了这个问题;现在你可以这样写:

void g()
{
  f(nullptr); //calls f(char*)
}

其他回答

Nullptr不能赋值给整型类型,比如int型,只能赋值给指针类型;内置指针类型,如int *ptr或智能指针,如std::shared_ptr<T>

我相信这是一个重要的区别,因为NULL仍然可以被赋给整型和指针,因为NULL是一个扩展到0的宏,可以作为int的初始值以及指针。

其他语言有保留词,它们是类型的实例。例如,Python:

>>> None = 5
  File "<stdin>", line 1
SyntaxError: assignment to None
>>> type(None)
<type 'NoneType'>

这实际上是一个相当接近的比较,因为None通常用于尚未初始化的东西,但与此同时,像None == 0这样的比较是假的。

另一方面,在普通C中,NULL == 0将返回真IIRC,因为NULL只是一个返回0的宏,这总是一个无效地址(AFAIK)。

0 used to be the only integer value that could be used as a cast-free initializer for pointers: you can not initialize pointers with other integer values without a cast. You can consider 0 as a consexpr singleton syntactically similar to an integer literal. It can initiate any pointer or integer. But surprisingly, you'll find that it has no distinct type: it is an int. So how come 0 can initialize pointers and 1 cannot? A practical answer was we need a means of defining pointer null value and direct implicit conversion of int to a pointer is error-prone. Thus 0 became a real freak weirdo beast out of the prehistoric era. nullptr was proposed to be a real singleton constexpr representation of null value to initialize pointers. It can not be used to directly initialize integers and eliminates ambiguities involved with defining NULL in terms of 0. nullptr could be defined as a library using std syntax but semantically looked to be a missing core component. NULL is now deprecated in favor of nullptr, unless some library decides to define it as nullptr.

NULL need not to be 0. As long you use always NULL and never 0, NULL can be any value. Asuming you programme a von Neuman Microcontroller with flat memory, that has its interrupt vektors at 0. If NULL is 0 and something writes at a NULL Pointer the Microcontroller crashes. If NULL is lets say 1024 and at 1024 there is a reserved variable, the write won't crash it, and you can detect NULL Pointer assignments from inside the programme. This is Pointless on PCs, but for space probes, military or medical equipment it is important not to crash.

根据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)