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?

È stato utile?

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 tipo std::nullptr_t è un puntatore nullo costante, e che un puntatore nullo costante integrale può essere convertito in std::nullptr_t. La direzione opposta non è consentito. Questo permette di sovraccaricare una funzione sia per i puntatori e interi, e passando nullptr per selezionare la versione puntatore. Passando NULL o 0 avrebbe confusamente selezionare la versione int.

  • Un cast di nullptr_t ad un tipo intero ha bisogno di un reinterpret_cast, e ha la stessa semantica come un getto di (void*)0 in un tipo integrale (attuazione mappatura definito). Un reinterpret_cast non può convertire nullptr_t a qualsiasi tipo di puntatore. Contare sulla conversione implicita, se possibile, o l'uso static_cast.

  • Il Principio richiede che sizeof(nullptr_t) essere sizeof(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:

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 tipo std::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.

Non

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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top