C++11:nullptr

C++11:nullptr,第1张

引言

C++98标准定义空指针是NULL,NULL是一个Macro常量,定义格式:

#ifdef __cplusplus  
#define NULL 0
#else  
#define NULL ((void *)0)  
#endif

如此定义的原因是,C语言允许void*隐式转换为任意指针类型,而C++不允许这样 *** 作,但是C++允许任意指针赋值0。

nullptr是C++11新引入的特性,C++98标准NULL的升级版本。虽然nullptr是C++11引入的关键字,但是和98版本不同的是,nullptr存在类型,它的类型是nullptr_t。需要特殊说明的是nullptr_t 是C++11~C++20标准特性,但不是C99 ~C17标准的特性,C语言从C23才开始支持nullptr_t。

nullptr

nullptr是一个标准的右值常量。C++11~C++20标准规定,nullptr具有下述特性:

  • nullptr可以隐式的方式转换为任何指针类型和任何成员指针类型的空指针。例如:
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赋值给左值引用
  • 由于nullptr是一个右值,因此不能对nullptr做取地址运算,例如:
std::nullptr_t* p1 = &nullptr; // 编译报错,不允许对nullptr做取地址运算
  • 对指针做空指针判断bool运算,nullptr运算结果为false,例如:
int * p = nullptr;    // nullptr可以赋值给int*
if (!p) 
{
	std::cout << "p空指针运算结果为false..";
}
  • nullptr内存大小等同于void*,也就是说sizeof(nullptr) === sizeof(void*),例如:
if (sizeof(nullptr) == sizeof(void*))
{
	std::cout << "sizeof(nullptr) == sizeof(void*)\n";
}
  • 为向后兼容,C++11依然允许使用NULL表示空指针,因此表达式nullptr == NULL为true,但是nullptr并不是0。也不能隐式转换为整数。
if (nullptr == NULL)
{
	std::cout << "nullptr == NULL为true \n";
}
nullptr_t

nullptr_t数据类型存在于头文件 ,具体声明:

#ifdef __cplusplus
namespace std
{
    typedef decltype(nullptr) nullptr_t;
}

using ::std::nullptr_t;
#endif

C++标准允许我们通过nullptr_t定义自己的nullptr,而且任何nullptr_t对象与nullptr,作==运算均为true,作!=运算均为false。例如:

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
nullptr_t的实现

基于上述nullptr和nullptr_t的讨论分析,笔者总结出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()
}

根据nullptr和nullptr_t的特性,可以逐个验证:

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";
}
nullptr的好处 解决整数0和空指针混淆

在C++11之前,NULL就是整数0,因此NULL既可表示整数0,也可表示空指针。这有时候会给我带来
一些混淆。例如:

void foo(int) {}
void foo(int*) {}

foo(0);     // 调用foo(int)而不是foo(int*)
foo(NULL);  // 如果NULL是0,则调用foo(int);

我们期望foo(NULL)调用函数是 foo(int*),但是实际上调用的是foo(int)。而nullptr可以很好的解决此问题。这也是nullptr产生的动机。

捕获空指针

空指针nullptr的类型为nullptr_t,我们可以通过nullptr_t类型捕获空指针。例如:

try 
{
	...
	throw nullptr;
}
catch (nullptr_t)
{
	...
}

但是,如果throw NULL,那我们用什么类型来catch呢?用int类型来catch?是不是有点别扭。

总结

nullptr是一个有类型的右值常量,可很好的解决了整数0和空指针的问题。所以建议大家在开发过程中优先使用nullptr替代NULL。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/3002147.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-09-27
下一篇 2022-09-27

发表评论

登录后才能评论

评论列表(0条)

保存