Che cosa esattamente è nullptr?
Domanda
Ora abbiamo C ++ 11 con molte nuove caratteristiche. Un interessante e confuso (almeno per me) è il nuovo nullptr
.
Bene, più alcuna necessità per il brutto NULL
macro.
int* x = nullptr;
myclass* obj = nullptr;
Ancora, io non sono sempre come funziona nullptr
. Ad esempio, articolo Wikipedia dice:
C ++ 11 corregge questa introducendo un nuovo parola per servire come distinta costante puntatore nullo: nullptr. Si tratta di tipo nullptr_t , che è implicitamente convertibile e paragonabile a qualsiasi tipo di puntatore o puntatore-a-membro tipo. Non è implicitamente convertibile o paragonabile a tipi interi, tranne che per bool.
Come è una parola chiave e un'istanza di un tipo?
Inoltre, hai un altro esempio (accanto alla Wikipedia uno) dove nullptr
è superiore al buon vecchio 0
?
Soluzione
Come è una parola chiave e un'istanza di un tipo?
Questo non è sorprendente. Sia true
e false
sono le parole chiave e come letterali hanno un tipo (bool
). nullptr
è un puntatore letterale di tipo std::nullptr_t
, ed è un prvalue (non si può prendere l'indirizzo usando &
).
-
4.10
sulla conversione puntatore dice che un prvalue di tipostd::nullptr_t
è un puntatore nullo costante, e che un puntatore nullo costante integrale può essere convertito instd::nullptr_t
. La direzione opposta non è consentito. Questo permette di sovraccaricare una funzione sia per i puntatori e interi, e passandonullptr
per selezionare la versione puntatore. PassandoNULL
o0
avrebbe confusamente selezionare la versioneint
. -
Un cast di
nullptr_t
ad un tipo intero ha bisogno di unreinterpret_cast
, e ha la stessa semantica come un getto di(void*)0
in un tipo integrale (attuazione mappatura definito). Unreinterpret_cast
non può convertirenullptr_t
a qualsiasi tipo di puntatore. Contare sulla conversione implicita, se possibile, o l'usostatic_cast
. -
Il Principio richiede che
sizeof(nullptr_t)
esseresizeof(void*)
.
Altri suggerimenti
nullptr: A Tipo-sicura e netta Null Pointer :
Il nuovo C ++ 09 nullptr parola designa una costante rvalue che serve come un puntatore nullo universale letterale, sostituendo il passeggino e debolmente tipizzato letterale 0 e la macro NULL infame. nullptr mette così fine a più di 30 anni di imbarazzo, l'ambiguità, e gli insetti. Le sezioni seguenti presentano l'impianto nullptr e mostrano come si può rimediare ai disturbi di NULL e 0.
Altri riferimenti:
- Wikibooks , con codice di esempio.
- Qui a Stack Overflow: Do you utilizzare NULL o 0 (zero) per i puntatori in C ++?
-
template
- gruppo Google: comp.lang.c ++ moderato. - discussione compilatore
Quando si dispone di una funzione che può ricevere puntatori a più di un tipo, chiamandola con NULL
è ambiguo. Il modo in cui questo viene lavorato intorno ora è molto hacky accettando un int e supponendo che è 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); }
};
In C++11
si sarebbe in grado di sovraccaricare in modo che nullptr_t
ptr<T> p(42);
sarebbe un errore di compilazione, piuttosto che un assert
run-time.
ptr(std::nullptr_t) : p_(nullptr) { }
Perché nullptr in C ++ 11? Che cos'è? Perché NULL non è sufficiente?
C ++ esperto Alex Allain dice perfettamente qui :
" ... immaginate di avere le due seguenti dichiarazioni di funzione:
void func(int n);
void func(char *s);
func( NULL ); // guess which function gets called?
Anche se sembra che la seconda funzione sarà chiamato - si è, dopo tutto, passando in quello che sembra essere un puntatore - è davvero la prima funzione che verrà chiamata! Il guaio è che perché NULL è 0 e 0 è un numero intero, la prima versione di func sarà chiamato al posto. Questo è il genere di cose che, sì, non accade tutto il tempo, ma quando si accade, è estremamente frustrante e confusa. Se non si conoscono i dettagli di ciò che sta accadendo, potrebbe anche sembrare un bug del compilatore. Una caratteristica del linguaggio che assomiglia ad un bug del compilatore è, beh, non qualcosa che si desidera.
Invio nullptr. In C ++ 11, nullptr è una nuova parola chiave che può (e dovrebbe!) Essere utilizzato per rappresentare puntatori NULL; in altre parole, ovunque si stesse scrivendo NULL prima, si dovrebbe usare nullptr invece. Non è più chiaro a voi, il programmatore , (tutti sanno cosa significa NULL), ma è più esplicito al compilatore , che non sarà più vedere 0s ovunque utilizzato per hanno un significato speciale se usato come un puntatore ".
nullptr
non può essere assegnato a un tipo integrale come un int
esclusivamente del tipo puntatore; sia dotato di un tipo di puntatore come int *ptr
o un puntatore intelligente come std::shared_ptr<T>
Credo questa è una distinzione importante perché NULL
può ancora essere assegnato sia un tipo integrale e un puntatore come NULL
è una macro espansa a 0
che può servire sia come un valore iniziale per un int
così come un puntatore.
Bene, altre lingue hanno riservato parole che sono istanze di tipi. Python, per esempio:
>>> None = 5
File "<stdin>", line 1
SyntaxError: assignment to None
>>> type(None)
<type 'NoneType'>
Questo è in realtà un confronto abbastanza vicino perché None
è in genere utilizzato per qualcosa che non è stata inizializzata, ma allo stesso tempo il confronto, come None == 0
sono false.
D'altra parte, in pianura C, NULL == 0
sarebbe tornato vero IIRC perché NULL
è solo una macro di ritorno 0, che è sempre un indirizzo non valido (per quanto ne so).
Inoltre, hai un altro esempio (accanto alla Wikipedia uno) dove
nullptr
è superiore al buon vecchio 0?
Sì. E 'anche un (semplificato) esempio del mondo reale che si è verificato nel nostro codice di produzione. Si trovava solo perché gcc è stato in grado di emettere un avviso quando crosscompilando ad una piattaforma con diversa larghezza registro (ancora non è sicuro esattamente perché solo quando crosscompilando da x86_64 a 86, mette in guardia warning: converting to non-pointer type 'int' from NULL
):
Si consideri questo codice (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;
}
Si produce questo output:
(a == pb): 1
(a == 0): 0
(a == NULL): 0
(a == null): 1
Si tratta di una parola chiave perché lo standard specificherà come tale. ;-) Secondo l'ultima bozza pubblica (n2914)
2.14.7 Pointer letterali [lex.nullptr]
pointer-literal: nullptr
Il puntatore letterale è la parola chiave
nullptr
. Si tratta di un rvalue di tipostd::nullptr_t
.
E 'utile perché non convertire implicitamente ad un valore integrale.
Diciamo che si dispone di una funzione (f) che è sovraccarico di prendere sia int e char *. Prima di C ++ 11, Se si voleva chiamare con un puntatore nullo, ed è stato utilizzato NULL (vale a dire il valore 0), allora si chiamava quello overload per int:
void f(int);
void f(char*);
void g()
{
f(0); // Calls f(int).
f(NULL); // Equals to f(0). Calls f(int).
}
Questa non è probabilmente quello che si voleva. C ++ 11 risolve questo con nullptr; Ora è possibile scrivere la seguente:
void g()
{
f(nullptr); //calls f(char*)
}
0 usato per essere l'unico valore intero che potrebbe essere utilizzato come un inizializzatore priva di cast per i puntatori: Non è possibile inizializzare i puntatori con altri valori interi senza un cast.
Si può considerare 0 come Singleton consexpr sintatticamente simile a un intero letterale. Si può iniziare qualsiasi puntatore o un numero intero. Ma sorprendentemente, vi accorgerete che non ha tipo distinto: si tratta di un int
. Allora come mai in grado di inizializzare 0 puntatori e 1 non può? Una risposta pratica è necessario un mezzo per definire puntatore valore nullo e conversione implicita diretta di int
a un puntatore è soggetto a errori. Così 0 è diventato un vero e proprio strano bestia Freak Out di epoca preistorica.
nullptr
stato proposto come una rappresentazione constexpr vera singoletto di valore nullo per inizializzare puntatori. Non può essere utilizzata per inizializzare direttamente interi ed elimina le ambiguità coinvolte di definire NULL
in termini di 0. nullptr
potrebbe essere definita come una libreria utilizzando la sintassi std ma semanticamente sembrava essere un componente fondamentale mancante.
NULL
è ora sconsigliata a favore della nullptr
, a meno che qualche biblioteca decide di definirla come nullptr
.
Ecco l'intestazione 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
(una grande quantità può essere scoperto con un rapido grep -r /usr/include/*`
)
Una cosa che salta fuori è l'overload dell'operatore *
(ritorno 0 è molto più amichevole di quanto va in segfault ...).
Un'altra cosa è che non sembra compatibile con la memorizzazione di un indirizzo di a tutti . Il che, rispetto a come va imbracatura vuoto s * 'e passando risultati NULL di puntatori normali valori sentinella, ridurrebbe ovviamente il 'non dimenticare mai, potrebbe essere una bomba' fattore.
NULL necessario essere 0. Finché si utilizza sempre NULL e mai 0, NULL può essere qualsiasi valore. Asuming si programma un von Neuman microcontrollore con memoria piatta, che ha le sue vektors di interruzione a 0. Se NULL è 0 e qualcosa scrive in un puntatore nullo il crash per microcontrollori. Se NULL è diciamo 1024 e 1024 v'è una variabile riservata, la scrittura non andrà in crash, e si può rilevare le assegnazioni Null Pointer dall'interno del programma. Questo è inutile su PC, ma anche per le sonde spaziali, materiale militare o medico è importante non crash.