const
ist eine Funktion, die von C++-Entwicklern seit Jahrzehnten für gute Dienste geschätzt wird, um Code robuster zu machen, indem versehentliche Änderungen verhindert werden.
Smart Pointer gibt es auch schon seit langer Zeit und haben den Lebenszyklus vieler Objekte sowie die Lebensbilanz vieler Entwickler im Laufe der Jahre vereinfacht.
auto
ist ein neueres Feature (C++11), das entwickelt wurde, um Code einfacher zu machen, und es wird seit Jahren beworben, damit wir es fast immer verwenden.
Also, wenn wir auto
eingeben , const
und einem intelligenten Zeiger zusammen, sollten wir erwarten, dass es eine großartige Mischung aus einfachem, robustem und ausdrucksstarkem Code produziert.
Diese Kombination kann jedoch eher zu irreführendem als zu aussagekräftigem Code führen. Wie in Code, der so aussieht, als würde er etwas tun, es aber nicht tut. Und betrügerischer Code ist eine der gefährlichsten Arten von Code.
auto + konstant + Zeiger
Beim Deklarieren eines Objekts mit auto
und const
impliziert, dass das Objekt tatsächlich const
ist :
auto const numbers = std::vector<int>{1, 2, 3, 4, 5};
Der obige Vektor numbers
ist const
:Wir können nichts hinzufügen, entfernen oder ändern, sonst würde der Code nicht kompiliert. Wenn dieser Vektor als Eingabe gedacht ist, hindert er uns daran, ihn versehentlich zu ändern und einen Fehler zu erzeugen.
Betrachten Sie nun den folgenden Fall:Zuweisen eines Zeigers in einen auto const
Wert:
Thing* getSomething(); auto const thing = getSomething();
Wie sieht dieser Code aus? Da steht, dass thing
ist const
. Aber thing
ist ein Zeiger, was bedeutet, dass thing
kann auf nichts anderes zeigen als auf getSomething
ist zurückgekommen. Dies ist das Äquivalent zu:
Thing* const thing = getSomething();
Der Zeiger ist konstant, aber nicht der Wert, auf den er zeigt.
Aber bei Verwendung von thing
Interessieren Sie sich im Geschäftscode wirklich für den Wert des Zeigers? Wenn der Sinn der Verwendung von thing
ist es, das Objekt zu erreichen, auf das es zeigt, wie es oft der Fall ist, Sie tun es nicht. Die Rolle von thing
soll das Objekt verkörpern, auf das gezeigt wird, und es kommt vor, dass Sie einen Zeiger erhalten, um es zu manipulieren.
Daher sieht es für mich danach aus, dass der Code darauf hindeutet, dass wir einen const
manipulieren Thing
, und kein const
Zeiger auf Thing
. Dies ist zwar nicht der Fall, aber beim Lesen von Code überprüfen Sie nicht jeden Prototyp jeder aufgerufenen Funktion. Umso mehr, wenn der Prototyp von getSomething
befindet sich nicht in unmittelbarer Nähe (was im Allgemeinen nicht der Fall ist):
auto const thing = getSomething();
Dieser Code schreit, dass Sie durch einen schreibgeschützten thing
geschützt sind , während es nur ein schreibgeschützter Zeiger auf ein änderbares Objekt ist. Täuscht es Sie nicht?
Eine Möglichkeit, dieses Problem zu umgehen, könnte darin bestehen, auto const*
zu verwenden , um das Objekt, auf das gezeigt wird, const
zu machen :
auto const* thing = getSomething();
Oder ist es ein Fall, dass die ungarische Notation zurückkommt?
auto const pThing = getSomething();
Ew, nein, wir mögen die ungarische Schreibweise nicht.
Aber Sie denken vielleicht, wer gibt überhaupt einen rohen Zeiger von einer Funktion zurück? Wir haben sogar die Möglichkeit erwähnt, Rohzeiger aus C++ zu entfernen (okay, es war am 1. April, aber trotzdem kam die Idee nicht aus dem Nichts). Wir sollten jetzt Smart Pointer verwenden, oder?
Richtig, wir sollten. Aber erstens gibt es immer noch veralteten Code, der noch nicht aufgeholt hat, und man kann mit Sicherheit sagen, dass es noch eine Weile dauern wird.
Und zweitens leiden Smart Pointer unter dem gleichen Problem, aber schlimmer. Mal sehen, warum.
auto + konstant + intelligenter Zeiger
Lassen Sie uns die Schnittstelle von getSomething
modernisieren und lassen Sie es einen intelligenten Zeiger zurückgeben, um auszudrücken, dass es den Besitz des Objekts an seinen Aufrufer abgibt:
std::unique_ptr<Thing> getSomething();
Unser Aufrufcode sieht so aus:
auto const thing = getSomething();
Auch wenn der Code in Bezug auf die Eigentümerschaft viel robuster ist, in Bezug auf das, was const
ist und was nicht, die Situation ist identisch mit der mit rohen Zeigern.
Tatsächlich ist der Smart Pointer im obigen Code const
, was uns selten interessiert, aber das Objekt, auf das es zeigt, nicht. Und der Code vermittelt dieses falsche Gefühl des Schutzes, indem er einen vorbeigehenden Leser zu der Annahme verleitet, dass das Objekt, das wirklich vom Code verwendet wird (wahrscheinlich der Thing
der intelligente Zeiger zeigt) ist const
und dass alles sicher ist.
Was bei intelligenten Zeigern noch schlimmer ist, ist, dass es keine Möglichkeit gibt, Informationen um den auto
hinzuzufügen . Mit einem rohen Zeiger könnten wir auf Folgendes zurückgreifen:
auto const* thing = getSomething();
Aber mit einem intelligenten Zeiger können wir das nicht.
In diesem Fall schätze ich, dass die beste Option darin besteht, den const
zu entfernen zusammen, um Verwirrung zu vermeiden:
std::unique_ptr<Thing> getSomething(); auto thing = getSomething();
Haben Sie dieses Problem in Ihrem Code festgestellt? Wie sind Sie vorgegangen? Alle Ihre Kommentare sind willkommen.
Das könnte dir auch gefallen
- Kluge Entwickler verwenden intelligente Zeiger
- Die beeindruckende const-Referenz, die nicht const ist