introduction
The C++98 standard defines that the null pointer is NULL, and NULL is a Macro constant. The definition format is:
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
The reason for this definition is that the C language allows void* to be implicitly converted to any pointer type, while C++ does not allow this operation, but C++ allows any pointer to be assigned a value of 0.
Nullptr is a newly introduced feature of C++11, an upgraded version of C++98 standard NULL. Although nullptr is a keyword introduced by C++11, unlike the 98 version, nullptr has a type, and its type is nullptr_t. It should be noted that nullptr_t is a standard feature of C++11~C++20, but not a feature of C99~C17 standard. C language only supports nullptr_t from C23.
nullptr
nullptr is a standard rvalue constant. The C++11~C++20 standard stipulates that nullptr has the following characteristics:
- nullptr can be implicitly converted to a null pointer of any pointer type and any member pointer type. For example:
int * p = nullptr; // nullptr可以赋值给int*
std::string * p2 = nullptr; // nullptr可以赋值给std::string*
std::nullptr_t&& n = nullptr; // nullptr可以赋值给std::nullptr_t右值引用
std::nullptr_t& nn = nullptr; // 编译报错,不允许nullptr赋值给左值引用
- Since nullptr is an rvalue, the address operation cannot be performed on nullptr, for example:
std::nullptr_t* p1 = &nullptr; // 编译报错,不允许对nullptr做取地址运算
- Make a null pointer judgment bool operation on the pointer, and the result of the nullptr operation is false, for example:
int * p = nullptr; // nullptr可以赋值给int*
if (!p)
{
std::cout << "p空指针运算结果为false..";
}
- The memory size of nullptr is equivalent to void*, that is to say sizeof(nullptr) === sizeof(void*), for example:
if (sizeof(nullptr) == sizeof(void*))
{
std::cout << "sizeof(nullptr) == sizeof(void*)\n";
}
- For backward compatibility, C++11 still allows NULL to represent a null pointer, so the expression nullptr == NULL is true, but nullptr is not 0. There is also no implicit conversion to an integer.
if (nullptr == NULL)
{
std::cout << "nullptr == NULL为true \n";
}
nullptr_t
The nullptr_t data type exists in the header file <stddef.h>, specifically declared:
#ifdef __cplusplus
namespace std
{
typedef decltype(nullptr) nullptr_t;
}
using ::std::nullptr_t;
#endif
The C++ standard allows us to define our own nullptr through nullptr_t, and any nullptr_t object and nullptr are true for == operations, and false for != operations. For example:
std::nullptr_t n = std::nullptr_t();
if (n == nullptr)
{
std::cout << "n == nullptr is true\n";
}
if (!(n != nullptr))
{
std::cout << "n != nullptr is false\n";
}
(n == nullptr) is true
(n != nullptr) is false
Implementation of nullptr_t
Based on the discussion and analysis of nullptr and nullptr_t above, the author concludes the possible realization of nullptr_t:
namespace ptr
{
struct nullptr_t
{
void* __lx;
inline constexpr nullptr_t() : __lx(0) {
}
inline constexpr nullptr_t(int) : __lx(0) {
}
template <class _Tp>
inline constexpr operator _Tp* () const {
return 0; }
template <class _Tp, class _Up>
inline constexpr operator _Tp _Up::* () const {
return 0; }
nullptr_t* operator&() const = delete;
friend inline constexpr bool operator==(nullptr_t, nullptr_t) {
return true; }
friend inline constexpr bool operator!=(nullptr_t, nullptr_t) {
return false; }
};
inline constexpr nullptr_t __get_nullptr_t() {
return nullptr_t(0); }
#define nullptr __get_nullptr_t()
}
According to the characteristics of nullptr and nullptr_t, it can be verified one by one:
int * p = ptr::nullptr; // nullptr可以赋值给int*
ptr::nullptr_t&& n = ptr::nullptr; // nullptr可以赋值给std::nullptr_t右值引用
ptr::nullptr_t* pp = &ptr::nullptr; // 编译报错,“ptr::nullptr_t *ptr::nullptr_t::operator &(void) const”: 尝试引用已删除的函数
ptr::nullptr_t nn = ptr::nullptr_t();
if (nn == ptr::nullptr)
{
std::cout << "(n == nullptr) is true\n";
}
if (!(nn != ptr::nullptr))
{
std::cout << "(n != nullptr) is false\n";
}
if (ptr::nullptr == NULL)
{
std::cout << "nullptr == NULL为true \n";
}
Benefits of nullptr
Solve integer 0 and null pointer confusion
Before C++11, NULL was the integer 0, so NULL can represent either the integer 0 or a null pointer. This can sometimes cause us some confusion. For example:
void foo(int) {
}
void foo(int*) {
}
foo(0); // 调用foo(int)而不是foo(int*)
foo(NULL); // 如果NULL是0,则调用foo(int);
We expect foo(NULL) to call foo(int*) , but it actually calls foo(int) . And nullptr can solve this problem very well. This is also the motivation for nullptr.
catch null pointer
The type of the null pointer nullptr is nullptr_t, and we can capture the null pointer through the nullptr_t type. For example:
try
{
...
throw nullptr;
}
catch (nullptr_t)
{
...
}
However, if throw NULL, what type do we use to catch? Use int type to catch? Is it a bit awkward.
Summarize
nullptr is a typed rvalue constant, which can solve the problems of integer 0 and null pointer. Therefore, it is recommended that you use nullptr instead of NULL in the development process.