669 Stimmen

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, kein Grund mehr für das unangenehme Makro NULL .

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

Dennoch verstehe ich nicht, wie nullptr Werke. Zum Beispiel, Wikipedia-Artikel sagt:

C++11 korrigiert dies durch die Einführung einer neuen Stichwort als unterscheidbare Null-Zeiger-Konstante zu verwenden: nullptr. Sie ist von Typ nullptr_t der implizit konvertierbar und mit jedem Zeigertyp oder Zeiger-auf-Glied-Typ vergleichbar ist. Er ist nicht implizit konvertierbar oder vergleichbar mit ganzzahligen Typen, mit Ausnahme von bool.

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

Haben Sie noch ein weiteres Beispiel (neben dem aus Wikipedia), in dem nullptr ist besser als die gute alte 0 ?

7voto

Gabriel Schreiber Punkte 2086

Haben Sie noch ein weiteres Beispiel (neben dem aus Wikipedia), in dem nullptr der guten alten 0 überlegen ist?

Ja. Es handelt sich auch um ein (vereinfachtes) Beispiel aus der Praxis, das in unserem Produktionscode vorkam. Es fiel nur auf, weil gcc in der Lage war, eine Warnung auszugeben, wenn er auf eine Plattform mit anderer Registerbreite crosskompilierte (ich bin mir immer noch nicht sicher, warum nur beim Crosskompilieren 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;
}

Das Ergebnis ist diese Ausgabe:

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

6voto

Mark Rushakoff Punkte 236626

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 ein ziemlich genauer Vergleich, denn None wird typischerweise für etwas verwendet, das noch nicht intialisiert wurde, aber gleichzeitig werden Vergleiche wie None == 0 falsch sind.

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

5voto

Zunächst möchte ich Ihnen eine Implementierung von einfachen 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 ist ein subtiles Beispiel für Rückgabe Typ Resolver Idiom, um automatisch einen Null-Zeiger des richtigen Typs abzuleiten, abhängig vom Typ der Instanz, der er zugewiesen wird.

int *ptr = nullptr;                // OK
void (C::*method_ptr)() = nullptr; // OK
  • Wie Sie oben sehen können, wenn nullptr einem Integer-Zeiger zugewiesen wird, ein int Typinstanziierung der schablonenhaften Konvertierungsfunktion erstellt wird. Dasselbe gilt auch für Methodenzeiger.
  • Auf diese Weise, indem wir die Vorlagenfunktionalität nutzen, erstellen wir jedes Mal, wenn wir eine neue Typzuweisung vornehmen, den entsprechenden Typ des Nullzeigers.
  • Als nullptr ein Integer-Literal mit dem Wert Null ist, können Sie seine Adresse nicht verwenden, was wir durch Löschen des &-Operators erreicht haben.

Warum brauchen wir nullptr in erster Linie?

  • Sie sehen traditionelle NULL hat einige Probleme damit, wie unten beschrieben:

1 Implizite Umwandlung

char *str = NULL; // Implicit conversion from void * to char *
int i = NULL;     // OK, but `i` is not pointer type

2 Mehrdeutigkeit von Funktionsaufrufen

void func(int) {}
void func(int*){}
void func(bool){}

func(NULL);     // Which one to call?
  • Bei der Kompilierung tritt der folgende Fehler auf:

    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 Überlastung des Konstruktors

struct String
{
    String(uint32_t)    {   /* size of string */    }
    String(const char*) {       /* string */        }
};

String s1( NULL );
String s2( 5 );
  • In solchen Fällen benötigen Sie einen expliziten Cast (d.h.,  String s((char*)0)) .

3voto

KTC Punkte 8899

Es ist ein Schlüsselwort, weil es in der Norm als solches angegeben wird ;-) Laut dem letzten öffentlichen Entwurf (n2914)

2.14.7 Zeigerliterale [lex.nullptr]

pointer-literal:
nullptr

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

Sie ist nützlich, weil sie nicht implizit in einen ganzzahligen Wert umgewandelt wird.

3voto

Amit G. Punkte 2191

Nehmen wir an, Sie haben eine Funktion (f), die überladen ist und sowohl int als auch char* annehmen kann. Wenn Sie vor C++ 11 die Funktion mit einem Null-Zeiger aufrufen wollten und NULL (d.h. den Wert 0) verwendeten, würden Sie die für int überladene Funktion aufrufen:

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

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

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

void g()
{
  f(nullptr); //calls f(char*)
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X