我们现在拥有许多新功能的C ++ 11。一个有趣且令人困惑的问题(至少对我而言)是新的nullptr
。
嗯,不再需要令人讨厌的宏NULL
。
int* x = nullptr;
myclass* obj = nullptr;
尽管如此,我还没有了解nullptr
的工作原理。例如,Wikipedia article说:
C ++ 11通过引入一个新的关键字来纠正这一点,以作为一个区别的空指针常量:nullptr。它是类型nullptr_t ,它是可隐式转换的,可与任何指针类型或指向成员类型的类型相媲美。除了bool之外,它不可隐式转换或与整数类型相比。
它是一个关键字和一个类型的实例?
另外,你有另一个例子(在维基百科旁边),其中nullptr
优于好的0
吗?
答案 0 :(得分:377)
它是一个关键字和一个类型的实例?
这并不奇怪。 true
和false
都是关键字,文字也是类型(bool
)。 nullptr
是类型std::nullptr_t
的指针文字,它是一个prvalue(你不能使用&
获取它的地址)。
4.10
表示类型std::nullptr_t
的prvalue是空指针常量,并且整数空指针常量可以转换为std::nullptr_t
。不允许相反的方向。这允许为指针和整数重载函数,并传递nullptr
以选择指针版本。传递NULL
或0
会令人困惑地选择int
版本。
将nullptr_t
转换为整数类型需要reinterpret_cast
,并且具有与(void*)0
到整数类型(映射实现定义)的转换相同的语义。 reinterpret_cast
无法将nullptr_t
转换为任何指针类型。如果可能,请依靠隐式转换或使用static_cast
。
标准要求sizeof(nullptr_t)
为sizeof(void*)
。
答案 1 :(得分:56)
来自nullptr: A Type-safe and Clear-Cut Null Pointer:
新的C ++ 09 nullptr关键字指定一个rvalue常量,用作通用空指针文字,替换错误和弱类型的文字0和臭名昭着的NULL宏。因此,nullptr结束了超过30年的尴尬,模棱两可和错误。以下部分介绍了nullptr工具,并展示了如何解决NULL和0的问题。
其他参考资料:
template
答案 2 :(得分:35)
当你有一个可以接收指向多个类型的指针的函数时,用NULL
调用它是不明确的。通过接受一个int并假设它是NULL
来解决这个问题的方法非常糟糕。
template <class T>
class ptr {
T* p_;
public:
ptr(T* p) : p_(p) {}
template <class U>
ptr(U* u) : p_(dynamic_cast<T*>(u)) { }
// Without this ptr<T> p(NULL) would be ambiguous
ptr(int null) : p_(NULL) { assert(null == NULL); }
};
在C++11
中,您可以在nullptr_t
上重载,这样ptr<T> p(42);
将是编译时错误,而不是运行时assert
。
ptr(std::nullptr_t) : p_(nullptr) { }
答案 3 :(得分:23)
C ++专家Alex Allain says it perfectly here:
&#34; ...假设您有以下两个函数声明:
void func(int n);
void func(char *s);
func( NULL ); // guess which function gets called?
虽然看起来第二个函数会被调用 - 毕竟,你传递的似乎是一个指针 - 它真的是第一个被调用的函数!麻烦的是因为NULL是0,而0是一个整数,所以func的第一个版本将被调用。这是一种事情,是的,不会一直发生,但是当它确实发生时,非常令人沮丧和困惑。如果你不知道发生了什么的细节,它可能看起来像编译器错误。 看起来像编译器错误的语言功能,不是你想要的。
输入nullptr。在C ++ 11中,nullptr是一个新的关键字,它可以(并且应该!)用于表示NULL指针; 换句话说,无论你以前写过NULL,你都应该使用nullptr。 你不再清楚,程序员,(每个人都知道NULL意味着什么),但它对编译器来说更明确,这将是当用作指针时,不再看到任何地方被用来具有特殊含义。&#34;
答案 4 :(得分:9)
nullptr
无法分配给integral type
,例如int,只能分配给pointer
类型;内置指针类型(如int *ptr
)或智能指针(如std::shared_ptr<T>
我认为这是一个重要的区别因为NULL
仍然可以分配给integral type
和pointer
,因为NULL
是一个扩展为0
的宏它既可以作为int
的初始值,也可以作为pointer
的初始值。
答案 5 :(得分:4)
好吧,其他语言保留了类型实例的单词。例如,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)。
答案 6 :(得分:4)
另外,你有另一个例子(在维基百科旁边),其中
nullptr
优于好老0吗?
是。它也是我们的生产代码中出现的(简化的)现实示例。它只是突出,因为gcc能够在交叉编译到具有不同寄存器宽度的平台时发出警告(仍然不确定为什么只有当从x86_64交叉编译到x86时,警告warning: converting to non-pointer type 'int' from NULL
):
考虑这段代码(C ++ 03):
#include <iostream>
struct B {};
struct A
{
operator B*() {return 0;}
operator bool() {return true;}
};
int main()
{
A a;
B* pb = 0;
typedef void* null_ptr_t;
null_ptr_t null = 0;
std::cout << "(a == pb): " << (a == pb) << std::endl;
std::cout << "(a == 0): " << (a == 0) << std::endl; // no warning
std::cout << "(a == NULL): " << (a == NULL) << std::endl; // warns sometimes
std::cout << "(a == null): " << (a == null) << std::endl;
}
它产生了这个输出:
(a == pb): 1
(a == 0): 0
(a == NULL): 0
(a == null): 1
答案 7 :(得分:3)
这是一个关键字,因为标准会指定它。 ;-)根据最新公开草案(n2914)
2.14.7指针文字[lex.nullptr]
pointer-literal: nullptr
指针文字是关键字
nullptr
。它是std::nullptr_t
类型的右值。
它很有用,因为它不会隐式转换为整数值。
答案 8 :(得分:3)
假设您有一个函数(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*)
}
答案 9 :(得分:2)
首先让我为您提供简单的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
是Return Type Resolver惯用法的一个细微示例,它根据要分配给它的实例的类型自动推断出正确类型的空指针。
int *ptr = nullptr; // OK
void (C::*method_ptr)() = nullptr; // OK
nullptr
分配给整数指针时,将创建模板化转换函数的int
类型实例。方法指针也是如此。nullptr
是值为零的整数文字,因此您无法使用通过删除&运算符完成的地址。nullptr
?NULL
出现了以下问题:char *str = NULL; // Implicit conversion from void * to char *
int i = NULL; // OK, but `i` is not pointer type
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
struct String
{
String(uint32_t) { /* size of string */ }
String(const char*) { /* string */ }
};
String s1( NULL );
String s2( 5 );
String s((char*)0))
。答案 10 :(得分:0)
0曾经是唯一可以用作指针的无转换初始化程序的整数值:你不能在没有强制转换的情况下用其他整数值初始化指针。
您可以将0视为consexpr singleton,在语法上类似于整数文字。它可以启动任何指针或整数。但令人惊讶的是,您会发现它没有明显的类型:它是int
。那么为什么0可以初始化指针而1不能?一个实际的答案是我们需要一种定义指针空值的方法,并且int
到指针的直接隐式转换容易出错。因此,在史前时代,0变成了一个真正怪异的怪兽。
nullptr
被提议是一个真正的单例constexpr表示null值来初始化指针。它不能用于直接初始化整数,并消除了以0为单位定义NULL
所涉及的歧义。nullptr
可以定义为使用std语法的库,但在语义上看起来是一个缺少的核心组件。
NULL
现已弃用nullptr
,除非某些图书馆决定将其定义为nullptr
。
答案 11 :(得分:0)
根据cppreference,nullptr
是一个关键字:
表示指针文字。它是类型为
std::nullptr_t
的prvalue。 从nullptr到的空指针值存在 隐式转换 任何指针类型 和 任何指向成员类型的指针 。相似的转换 对于任何包含以下类型的值的空指针常量都存在std::nullptr_t
以及宏NULL
。
nullptr
将隐式转换为任何指针类型,但不转换为整数。 NULL
是一个宏,它是实现定义的空指针常量。通常这样定义:
#define NULL 0
即整数。
因此:
int i = NULL; //OK
int i = nullptr; //error
int* p = NULL; //OK
int* p = nullptr; //OK
nullptr
可以避免两个函数重载时产生歧义:
void func(int x); //1)
void func(int* x); //2)
func(NULL)
之所以调用1),是因为NULL
是一个整数。
func(nullptr)
之所以调用2),是因为nullptr
不是整数,并且会隐式转换为任何指针类型。
使用nulptr的优点:
if (ptr == nullptr)
代替if (ptr == 0)
答案 12 :(得分:-1)
这是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/*`
可以发现很多东西)
跳出的一件事是运算符*
重载(返回0比段错误更友好...)。
另一件事是,它看起来根本与存储地址 不兼容。与将空*传递空值并将NULL结果作为哨兵值传递给普通指针相比,这显然会减少“永远不会忘记,这可能是炸弹”的因素。
答案 13 :(得分:-2)
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.