Die relevanten Anwendungsfälle für Nullzeiger sind
- Umleitung zu einem tieferen Baumknoten, der möglicherweise nicht vorhanden ist oder noch nicht verknüpft wurde. Das ist etwas, das Sie immer eng in einer speziellen Klasse zusammenfassen sollten, damit Lesbarkeit oder Prägnanz hier kein so großes Problem darstellen.
Dynamische Besetzungen. Das Umwandeln eines Basisklassenzeigers in eine bestimmte abgeleitete Klasse (etwas, das Sie erneut vermeiden sollten, das Sie jedoch manchmal für erforderlich halten) ist immer erfolgreich, führt jedoch zu einem Nullzeiger, wenn die abgeleitete Klasse nicht übereinstimmt. Eine Möglichkeit, dies zu überprüfen, ist
Derived* derived_ptr = dynamic_cast<Derived*>(base_ptr);
if(derived_ptr != nullptr) { ... }
(oder vorzugsweise auto derived_ptr = ...
). Dies ist schlecht, da der (möglicherweise ungültige, dh null) abgeleitete Zeiger außerhalb des if
Bereichs des Sicherheitsschutzblocks bleibt . Dies ist nicht erforderlich, da Sie in C ++ boolesche konvertierbare Variablen in eine if
Bedingung einführen können :
if(auto derived_ptr = dynamic_cast<Derived*>(base_ptr)) { ... }
Das ist nicht nur kürzer und bereichssicher, sondern auch viel klarer in seiner Absicht: Wenn Sie in einer separaten if-Bedingung nach null suchen, fragt sich der Leser: "OK, derived_ptr
darf hier also nicht null sein ... nun, warum sollte es ist null? " Während die einzeilige Version sehr deutlich sagt : „Wenn Sie sicher werfen können base_ptr
zu Derived*
, dann verwenden Sie es für ...“.
Das Gleiche funktioniert genauso gut für alle anderen Operationen mit möglichen Fehlern, die einen Zeiger zurückgeben. IMO sollten Sie dies jedoch generell vermeiden: Es ist besser, so etwas wie boost::optional
den "Container" für Ergebnisse möglicherweise fehlgeschlagener Operationen zu verwenden, als Zeiger.
Wenn also der Hauptanwendungsfall für Nullzeiger immer in einer Variation des impliziten Cast-Stils geschrieben werden sollte, würde ich sagen, dass es aus Konsistenzgründen gut ist , diesen Stil immer zu verwenden, dh ich würde mich für if(ptr)
mehr einsetzen if(ptr!=nullptr)
.
Ich fürchte, ich muss mit einer Anzeige enden: Die if(auto bla = ...)
Syntax ist eigentlich nur eine etwas umständliche Annäherung an die eigentliche Lösung solcher Probleme: Pattern Matching . Warum sollten Sie zuerst eine Aktion erzwingen (z. B. einen Zeiger werfen) und dann bedenken, dass möglicherweise ein Fehler vorliegt? Ich meine, es ist lächerlich, nicht wahr? Es ist wie, Sie haben etwas zu essen und möchten Suppe machen. Sie geben es Ihrem Assistenten mit der Aufgabe, den Saft zu extrahieren, falls es sich um ein weiches Gemüse handelt. Du siehst es dir nicht zuerst an. Wenn Sie eine Kartoffel haben, geben Sie sie immer noch Ihrem Assistenten, aber er schlägt sie Ihnen mit einem Fehlerbericht ins Gesicht. Ah, zwingende Programmierung!
Viel besser: Betrachten Sie sofort alle Fälle, auf die Sie stoßen könnten. Dann handeln Sie entsprechend. Haskell:
makeSoupOf :: Foodstuff -> Liquid
makeSoupOf p@(Potato{..}) = mash (boil p) <> water
makeSoupOf vegetable
| isSoft vegetable = squeeze vegetable <> salt
makeSoupOf stuff = boil (throwIn (water<>salt) stuff)
Haskell hat auch spezielle Werkzeuge für den Fall, dass wirklich ernsthafte Fehler möglich sind (sowie für eine ganze Reihe anderer Dinge): Monaden. Aber das ist nicht der Ort, um diese zu erklären.
⟨/Inserat⟩
0
oder zu testennullptr
. (NULL
ist ein C'ism und erfordert das Einfügen einer Header-Datei.)