NULL与nullptr

C++ 空指针

Posted by 邹盛富 on April 29, 2018

C中的NULL

一直很好奇C++为什么有NULL和nullptr两种值,所以就自己查了一些资料研究一下。原来早在1972年,C语言诞生的初期,常数0带有常数及空指针的双重身分。C使用preprocessor macro NULL表示空指针,让NULL及0分别代表空指针及常数0,在C语言中NULL实际被定义如下:

#define NULL ((void *)0)

也就是说NULL实际上是一种void *类型的指针,然后吧void *指针赋值给int *或者foo_t *的指针的时候,隐式转换成相应的类型。但是当使用C++编译器的时候,这样做是会出错的,因为C++是一种强类型的语言,这就导致void *类型不能够隐式转换成其他的指针类型,所以,编译器会提供如下的头文件定义NULL:

#ifdef __cplusplus ---简称:cpp c++ 文件
#define NULL 0
#else
#define NULL ((void *)0)
#endif

C++中的NULL

其实在C++11中没有引入nullptr之前,由于C++中不能将void *类型的指针隐式转换成其他指针类型,而又为了解决空指针的问题,在C++中使用0来表示空指针(其实NULL就是0),但是还是有如下的问题。比如我现在在一个类中定义了如下的函数:

void bar(sometype1 a, sometype2 *b);

同时在另外的函数中需要调用这个函数,调用方式如下: a文件

bar(a, b);

b文件

bar(a, 0);

上述代码可以安全的运行,但是,如果我需要对这个类型进行扩展,重载其中的函数,重载后的函数声明如下:

void bar(sometype1 a, sometype2 *b);
void bar(sometype1 a, int i);

这个时候文件中的b文件中的函数并没有按照期望的方式运行着,它会调用void bar(sometype1 a, int i)函数,可以通过修改调用函数的参数类型来使其调用void bar(sometype1 a, sometype2 *b)函数,代码如下:

bar(a, static_cast<sometype2 *>(0));

但是上述的方式看起来是非常的别扭,并不是那么的优美,所以C++11中引入了nullptr来解决这个问题。

C++11中的nullptr

关键词nullptr指代指针字面量,它是std::nullptr_t类型的纯右值。存在从nullptr到任何指针类型及任何指向成员指针类型的隐式转换,同样的转换对于任何空指针常量,空指针常量包含 std::nullptr_t的值,还有宏NULL

如果引入了nullptr上述的问题便可以以很简单的方式来解决,只需要在调用的时候使用nullptr代替0就可以了,代码便可以按照期望的方式运行。如果某些编译器不支持C++11,可以自己模拟实现一个nullptr,代码如下:

const
class nullptr_t
{
public:
    template<class T>
    inline operator T*() const
        { return 0; }

    template<class C, class T>
    inline operator T C::*() const
        { return 0; }

private:
    void operator&() const;
} nullptr = {};