现在c++ 11有了许多新特性。一个有趣而令人困惑的(至少对我来说)是新的nullptr。
不需要讨厌的宏NULL了。
int* x = nullptr;
myclass* obj = nullptr;
不过,我还是不明白nullptr是如何工作的。例如,维基百科的一篇文章说:
c++ 11通过引入一个新的关键字作为区分空指针常量nullptr来纠正这一点。它的类型为nullptr_t,可隐式转换,可与任何指针类型或指针到成员类型相比较。它不能隐式转换,也不能与整型相比,bool类型除外。
它如何既是关键字又是类型的实例?
此外,你是否有另一个例子(除了维基百科的一个),其中nullptr优于好旧的0?
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.
让我首先给您一个简单的nullptr_t的实现
struct nullptr_t
{
void operator&() const = delete; // Can't take address of nullptr
template<class T>
inline operator T*() const { return 0; }
template<class C, class T>
inline operator T C::*() const { return 0; }
};
nullptr_t nullptr;
nullptr是返回类型解析器习惯用法的一个微妙示例,它根据分配给实例的类型自动推导出正确类型的空指针。
int *ptr = nullptr; // OK
void (C::*method_ptr)() = nullptr; // OK
如上所述,当nullptr被赋值给整数指针时,将创建模板化转换函数的int类型实例化。方法指针也是一样。
通过这种方式利用模板功能,我们实际上每次都创建了适当类型的空指针,这是一个新的类型赋值。
因为nullptr是一个值为0的整型字面值,你不能使用它的地址,这是我们通过删除&操作符实现的。
为什么我们首先需要nullptr ?
你可以看到传统的NULL有一些问题,如下:
1️⃣隐式转换
char *str = NULL; // Implicit conversion from void * to char *
int i = NULL; // OK, but `i` is not pointer type
2️⃣函数调用歧义
void func(int) {}
void func(int*){}
void func(bool){}
func(NULL); // Which one to call?
编译会产生以下错误:
error: call to 'func' is ambiguous
func(NULL);
^~~~
note: candidate function void func(bool){}
^
note: candidate function void func(int*){}
^
note: candidate function void func(int){}
^
1 error generated.
compiler exit status 1
3️⃣构造函数重载
struct String
{
String(uint32_t) { /* size of string */ }
String(const char*) { /* string */ }
};
String s1( NULL );
String s2( 5 );
在这种情况下,需要显式强制转换(即String s((char*)0))。
这是LLVM头文件。
// -*- C++ -*-
//===--------------------------- __nullptr --------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef _LIBCPP_NULLPTR
#define _LIBCPP_NULLPTR
#include <__config>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
#pragma GCC system_header
#endif
#ifdef _LIBCPP_HAS_NO_NULLPTR
_LIBCPP_BEGIN_NAMESPACE_STD
struct _LIBCPP_TEMPLATE_VIS nullptr_t
{
void* __lx;
struct __nat {int __for_bool_;};
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR nullptr_t() : __lx(0) {}
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR nullptr_t(int __nat::*) : __lx(0) {}
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR operator int __nat::*() const {return 0;}
template <class _Tp>
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
operator _Tp* () const {return 0;}
template <class _Tp, class _Up>
_LIBCPP_INLINE_VISIBILITY
operator _Tp _Up::* () const {return 0;}
friend _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR bool operator==(nullptr_t, nullptr_t) {return true;}
friend _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR bool operator!=(nullptr_t, nullptr_t) {return false;}
};
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR nullptr_t __get_nullptr_t() {return nullptr_t(0);}
#define nullptr _VSTD::__get_nullptr_t()
_LIBCPP_END_NAMESPACE_STD
#else // _LIBCPP_HAS_NO_NULLPTR
namespace std
{
typedef decltype(nullptr) nullptr_t;
}
#endif // _LIBCPP_HAS_NO_NULLPTR
#endif // _LIBCPP_NULLPTR
(使用grep -r /usr/include/* '可以发现很多内容)
一个突出的东西是操作符* overload(返回0比分段故障友好得多……)
另一件事是,它看起来与存储地址根本不兼容。与抛出void*和将NULL结果作为哨兵值传递给普通指针的方式相比,这显然会减少“永远不要忘记,它可能是一个炸弹”的因素。