C++11 und die Null-Zeiger-Konstanten

von Hubert Schmid vom 2011-11-13

Gute Nachrichten für alle C++‑Entwickler: Die neue Sprachversion C++11 führt mit dem Schlüsselwort nullptr eine weitere und einfache Möglichkeit ein, Null-Zeiger-Konstanten anzugeben. Und im Unterschied zu den bereits existierenden Varianten ist das neue Schlüsselwort intuitiv und verhält sich auch so.

Diese Aussage überrascht möglicherweise Entwickler, die sich bisher nur oberflächlich mit C++ auseinandergesetzt haben. Aus diesem Grund hole ich ein wenig aus und beschreibe die unterschiedlichen Varianten.

Ich habe in meinem Code bisher ausschließlich 0 verwendet, wenn ich eine Null-Zeiger-Konstante benötigt habe. Dieses magische Symbol kann implizit in einen Null-Zeiger jedes Zeigertyps konvertiert werden und eignet sich aus diesem Grund relativ gut. Die Sache hat jedoch zwei offensichtliche Probleme: Erstens ist an dem Ausdruck nicht erkennbar, dass ein Zeiger initialisiert werden soll. Und zweitens kann es zu überraschenden Ergebnissen bei überladenen Funktionen kommen. Am folgenden Beispiel lässt sich das einfach nachvollziehen.

void foobar(const char*); void foobar(int);

Offensichtlich können beide Funktionen mit dem Ausdruck foobar(0) aufgerufen werden, wenn die jeweils andere Funktion nicht existieren würde. Etwas komplizierter ist die Situation, wenn beide Funktionen deklariert sind. Man könnte vermuten, dass der Compiler eine Fehlermeldung liefert, dass die aufgerufene Funktion nicht eindeutig bestimmt werden kann. Tatsächlich ist es aber so, dass die zweite Funktion aufgerufen wird, denn die Übereinstimmung der Parametertypen ist für diese Funktion exakt. Überraschend ist dieses Verhalten für den Entwickler, wenn er sich nicht über die Existenz der zweiten Funktion bewusst ist.

Aus C hat C++ das Makro NULL geerbt, das in C häufig für Null-Zeiger verwendet wird. Und für einen unerfahrenen Entwickler mag das wie die geeignete Lösung für Null-Zeiger-Konstanten aussehen. Doch er wird vermutlich überrascht sein, dass der Ausdruck foobar(NULL) ebenfalls die zweite Funktion aufruft. Das liegt schlicht und einfach daran, dass in C++ das Makro NULL effektiv als 0 definiert ist.

Da die Situation mit den Null-Zeiger-Konstanten in C++ bisher so unbefriedigend war, finden sich etliche Ansätze das Problem zu lösen. Erwähnenswert finde ich den Mechanismus des C++‑Compilers der GCC. Dort wird das Makro NULL magisch definiert, so dass es sich zwar wie 0 verhält, der Übersetzer aber zumindest eine Warnung ausgibt, wenn es nicht im Zeiger-Kontext verwendet wird. In dem gerade diskutierten Beispiel würde GCC beispielsweise die folgende Warnung ausgeben – bei entsprechend gesetzten Kommandozeilenoptionen.

warning: passing NULL to non-pointer argument 1 of ‘void foobar(int)’ [-Wconversion-null]

Der Aufruf der ersten Funktion ist in C++03 aber weiterhin kompliziert. Der folgende Ausschnitt zeigt dafür zwei mögliche Varianten.

// using an explicit cast to a pointer type foobar(static_cast<const char*>(0)); // using an intermediate variable (implicit cast) const char* null = 0; foobar(null);

Das neue Schlüsselwort nullptr löst das Problem nun endlich. Es verhält sich so, wie man das von den entsprechenden Schlüsselwörtern aus Sprachen wie Java und C# kennt. Wie 0 kann jeder Zeigertyp mit nullptr initialisiert werden, und auch die Vergleiche mit Zeigertypen verhalten sich entsprechend. Im Gegensatz zu 0 können numerische Typen jedoch nicht mit nullptr initialisiert werden. Damit kann sich der Ausdruck foobar(nullptr) nur so verhalten, wie man das intuitiv erwartet, das heißt er ruft die erste der beiden überladenen Funktionen auf.

Für mich bedeutet das, dass ich in neuem Code konsequent nur noch nullptr für Null-Zeiger-Konstanten verwenden werde. Die Vorteile sind offensichtlich. Und nennenswerte Nachteile – außer natürlich der allgegenwärtigen Interoperabilität mit Legacy-Code – sehe ich nicht.