it-swarm.com.de

Was genau ist nullptr?

Wir haben jetzt C++ 11 mit vielen neuen Funktionen. Eine interessante und verwirrende (zumindest für mich) ist die neue nullptr.

Nun, keine Notwendigkeit mehr für das fiese Makro NULL.

int* x = nullptr;
myclass* obj = nullptr;

Ich verstehe immer noch nicht, wie nullptr funktioniert. Zum Beispiel sagt Wikipedia-Artikel :

C++ 11 korrigiert dies, indem ein neues keyword eingeführt wird, das als definierte Nullzeiger-Konstante dient: nullptr. Es handelt sich um type nullptr_t, das implizit konvertierbar und mit jedem Zeigertyp oder Zeiger-zu-Mitglied-Typ vergleichbar ist. Es ist nicht implizit konvertierbar oder mit Integraltypen vergleichbar, mit Ausnahme von bool.

Wie ist es ein Schlüsselwort und eine Instanz eines Typs?

Haben Sie auch ein anderes Beispiel (neben der Wikipedia), wo nullptr dem guten alten 0 überlegen ist?

519
AraK

Wie ist es ein Schlüsselwort und eine Instanz eines Typs?

Das ist nicht überraschend. Sowohl true als auch false sind Schlüsselwörter und als Literale haben sie einen Typ (bool). nullptr ist ein Zeigerliteral vom Typ std::nullptr_t, und es ist ein prvalue (Sie können die Adresse nicht mit & verwenden). 

  • 4.10 über die Zeigerkonvertierung besagt, dass ein prvalue vom Typ std::nullptr_t eine Nullzeigerkonstante ist und dass eine integrierte Nullzeigerkonstante in std::nullptr_t konvertiert werden kann. Die entgegengesetzte Richtung ist nicht erlaubt. Dies ermöglicht das Überladen einer Funktion für Zeiger und Ganzzahlen sowie die Übergabe von nullptr zur Auswahl der Zeigerversion. Wenn Sie NULL oder 0 übergeben, wird die Version von int verwirrend ausgewählt. 

  • Eine Umwandlung von nullptr_t in einen ganzzahligen Typ erfordert einen reinterpret_cast und hat dieselbe Semantik wie eine Umwandlung von (void*)0 in einen ganzzahligen Typ (Mapping-Implementierung definiert). Ein reinterpret_cast kann nullptr_t nicht in einen Zeigertyp konvertieren. Verlassen Sie sich wenn möglich auf die implizite Konvertierung oder verwenden Sie static_cast

  • Der Standard verlangt, dass sizeof(nullptr_t)sizeof(void*) sein muss. 

370

Aus nullptr: Ein typsicherer und eindeutiger Nullzeiger :

Das neue C++ 09-Schlüsselwort nullptr bezeichnet eine rvalue-Konstante, die als universelles Nullzeiger-Literal dient und das fehlerhafte und schwach typisierte Literal 0 und das berüchtigte NULL-Makro ersetzt. nullptr macht damit mehr als 30 Jahre Peinlichkeit, Unklarheiten und Fehler aus. In den folgenden Abschnitten wird die Nullptr-Funktion dargestellt und dargestellt, wie die NULL- und 0-Beschwerden behoben werden können.

Weitere Referenzen:

53
nik

Wenn Sie über eine Funktion verfügen, die Zeiger auf mehrere Typen empfangen kann, ist der Aufruf mit NULL mehrdeutig. Die Art und Weise, wie dies jetzt umgangen wird, ist sehr hackig, wenn man ein int akzeptiert und davon ausgeht, dass es NULL ist.

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 könnten Sie nullptr_t überladen, so dass ptr<T> p(42); ein Fehler bei der Kompilierung wäre und keine Laufzeit assert.

ptr(std::nullptr_t) : p_(nullptr)  {  }
34
Motti

Warum nullptr in C++ 11? Was ist es? Warum reicht NULL nicht aus?

C++ - Experte Alex Allain sagt es perfekt hier :

"... stellen Sie sich vor, Sie haben die folgenden zwei Funktionsdeklarationen:

void func(int n); 
void func(char *s);

func( NULL ); // guess which function gets called?

Obwohl es so aussieht, als würde die zweite Funktion aufgerufen werden - Sie übergeben schließlich einen scheinbaren Zeiger - es ist wirklich die erste Funktion, die aufgerufen wird! Das Problem ist, dass, da NULL 0 ist und 0 eine ganze Zahl ist, die erste Version von func stattdessen aufgerufen wird. Dies ist die Art von Dingen, die ja nicht immer passiert, aber wenn es passiert, ist es extrem frustrierend und verwirrend. Wenn Sie die Details des Vorgangs nicht wussten, könnte dies wie ein Compiler-Fehler aussehen. Eine Sprachfunktion, die wie ein Compiler-Fehler aussieht, ist nicht etwas, das Sie wollen.

Geben Sie nullptr ein. In C++ 11 ist nullptr ein neues Schlüsselwort, das zur Darstellung von NULL-Zeigern verwendet werden kann (und sollte!). Mit anderen Worten, wo auch immer Sie NULL geschrieben haben, sollten Sie stattdessen nullptr verwenden. Es ist Ihnen nicht mehr klar, der Programmierer, (jeder weiß, was NULL bedeutet), , aber es ist explizit für den Compiler, der nicht mehr sieht, dass 0s überall verwendet werden, wenn sie als verwendet werden ein Zeiger. "

17
Gabriel Staples

nullptr kann keinem integral type wie einem int zugeordnet werden, sondern nur einem Typ pointer; entweder ein integrierter Zeigertyp wie int *ptr oder ein intelligenter Zeiger wie std::shared_ptr<T>

Ich glaube, das ist eine wichtige Unterscheidung, weil NULL immer noch einem integral type zugewiesen werden kann und eine pointer als NULL ein auf 0 erweitertes Makro ist, das sowohl als Anfangswert für eine int als auch als pointer dienen kann.

8
user633658

Haben Sie auch ein anderes Beispiel (neben der Wikipedia), wo nullptr der guten alten 0 überlegen ist?

Ja. Es ist auch ein (vereinfachtes) reales Beispiel, das in unserem Produktionscode vorkam. Es fiel nur auf, weil gcc beim Crosscompiling eine Warnung mit einer unterschiedlichen Registerbreite ausgeben konnte (immer noch nicht sicher, warum nur beim Crosscompiling von x86_64 nach x86, warnt warning: converting to non-pointer type 'int' from NULL)

Betrachten Sie diesen Code (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;
}

Es ergibt diese Ausgabe:

(a == pb): 1
(a == 0): 0
(a == NULL): 0
(a == null): 1
4

Nun, andere Sprachen haben reservierte Wörter, die Instanzen von Typen sind. Python zum Beispiel:

>>> None = 5
  File "<stdin>", line 1
SyntaxError: assignment to None
>>> type(None)
<type 'NoneType'>

Dies ist eigentlich ein ziemlich enger Vergleich, da None normalerweise für etwas verwendet wird, das nicht initialisiert wurde, aber gleichzeitig Vergleiche wie None == 0 falsch sind.

Auf der anderen Seite würde NULL == 0 in reiner C-Klasse true IIRC zurückgeben, da NULL nur ein Makro ist, das 0 zurückgibt, was immer eine ungültige Adresse (AFAIK) ist.

3
Mark Rushakoff

Es ist ein Schlüsselwort, weil der Standard es als solches spezifiziert. ;-) Nach dem neuesten öffentlichen Entwurf (n2914)

2.14.7 Zeigerliterale [Lex.nullptr]

pointer-literal:
nullptr

Das Zeigerliteral ist das Schlüsselwort nullptr. Es ist ein Wert vom Typ std::nullptr_t.

Dies ist nützlich, da es nicht implizit in einen ganzzahligen Wert konvertiert wird. 

3
KTC

Nehmen wir an, Sie haben eine Funktion (f), die überladen ist, um sowohl int als auch char * zu übernehmen. Wenn Sie es vor C++ 11 mit einem Nullzeiger aufrufen und NULL verwenden wollten (d. H. Den Wert 0), würden Sie die überladene für int aufrufen:

void f(int);
void f(char*);

void g() 
{
  f(0); // Calls f(int).
  f(NULL); // Equals to f(0). Calls f(int).
}

Dies ist wahrscheinlich nicht das, was Sie wollten. C++ 11 löst dies mit nullptr. Jetzt können Sie folgendes schreiben:

void g()
{
  f(nullptr); //calls f(char*)
}
2
Amit G.

Früher war 0 der einzige ganzzahlige Wert, der als umsetzungsfreier Initialisierer für Zeiger verwendet werden konnte: Sie können Zeiger ohne Umsetzung nicht mit anderen ganzzahligen Werten initialisieren. Sie können 0 als ein consexpr-Singleton betrachten, das syntaktisch einem Integer-Literal ähnelt. Es kann einen beliebigen Zeiger oder eine ganze Zahl initiieren. Aber überraschenderweise werden Sie feststellen, dass es keinen eindeutigen Typ gibt: Es ist ein int. Wie kommt es, dass 0 Zeiger initialisieren kann und 1 nicht? Eine praktische Antwort war, dass wir ein Mittel zum Definieren des Zeigernullwerts benötigen und die direkte implizite Konvertierung von int in einen Zeiger fehleranfällig ist. So wurde 0 aus der prähistorischen Zeit ein echtes Freak-Weirdo-Biest. Es wurde vorgeschlagen, nullptr eine echte Singleton-Constexpr-Darstellung des Nullwerts zu sein, um Zeiger zu initialisieren. Es kann nicht verwendet werden, um Ganzzahlen direkt zu initialisieren, und es beseitigt Mehrdeutigkeiten, die mit der Definition von NULL in Form von 0 verbunden sind. nullptr könnte als Bibliothek mit Standard-Syntax definiert werden, wird jedoch semantisch als fehlende Kernkomponente angesehen . NULL wird jetzt zu Gunsten von nullptr nicht mehr empfohlen, es sei denn, eine Bibliothek definiert es als nullptr.

0
Red.Wave