Effective C++
Verwenden Sie in Punkt 03 nach Möglichkeit const.
class Bigint
{
int _data[MAXLEN];
//...
public:
int& operator[](const int index) { return _data[index]; }
const int operator[](const int index) const { return _data[index]; }
//...
};
const int operator[]
macht einen Unterschied von int& operator[]
.
Aber was ist mit:
int foo() { }
und
const int foo() { }
Scheint so, als wären sie gleich.
Meine Frage ist, warum wir const int operator[](const int index) const
statt verwenden int operator[](const int index) const
?
int operator[](int i)
versusconst int operator[](const int i)
.Bigint&
oder habena const Bigint&
.const int i
Streiter haben sollte.Antworten:
Lebenslaufqualifizierer der obersten Ebene für Rückgabetypen, die nicht vom Klassentyp sind, werden ignoriert. Was bedeutet, dass selbst wenn Sie schreiben:
int const foo();
Der Rückgabetyp ist
int
. Wenn der Rückgabetyp eine Referenz ist, ist dies natürlichconst
nicht mehr die oberste Ebene, und die Unterscheidung zwischen:int& operator[]( int index );
und
int const& operator[]( int index ) const;
ist wichtig. (Beachten Sie auch, dass in Funktionsdeklarationen wie den oben genannten auch alle Lebenslaufqualifizierer der obersten Ebene ignoriert werden.)
Die Unterscheidung ist auch für Rückgabewerte vom Klassentyp relevant: Wenn Sie zurückgeben
T const
, kann der Aufrufer keine nicht konstanten Funktionen für den zurückgegebenen Wert aufrufen, z.class Test { public: void f(); void g() const; }; Test ff(); Test const gg(); ff().f(); // legal ff().g(); // legal gg().f(); // **illegal** gg().g(); // legal
quelle
()
nachff
und vergessengg
. Ich werde sie hinzufügen. Danke für die Verbesserung.const
ist nützlich, aber viele Leute scheinen sie entweder zu übersehen oder darauf zu bestehen, dass die Rückgabe einesconst
Wertes ohne Nutzen ist.int const
, die beispielsweise zurückgibt , einem Aufruf dieser Funktion entspricht, der für die Rückgabe geändert wurdeint
. Standardese in C ++ 11 §3.10 / 4, „Klassenwerte können cv-qualifizierte Typen haben; Nicht-Klassen-Werte haben immer cv-unqualifizierte Typen. “Sie sollten klar zwischen der Verwendung von const für Rückgabewerte, Parameter und der Funktion selbst unterscheiden.
Rückgabewerte
Überlegen Sie
const std::string SomeMethod() const
. Die(std::string&&)
Funktion kann nicht verwendet werden, da ein nicht konstanter Wert erwartet wird. Mit anderen Worten, die zurückgegebene Zeichenfolge wird immer kopiert.const
das zurückgegebene Objekt vor Änderungen geschützt.Parameter
Funktion selbst
const
am Ende steht, kann sie nur andereconst
Funktionen ausführen und keine Änderung oder Änderung von Klassendaten zulassen. Wenn es also als Referenz zurückgegeben wird, muss die zurückgegebene Referenz const sein. Es können nur const-Funktionen für ein Objekt oder einen Verweis auf ein Objekt aufgerufen werden, das const selbst ist. Auch die veränderlichen Felder können geändert werden.this
Verweis aufT const*
. Die Funktion kann immerconst_cast
this
, aber dies sollte natürlich nicht durchgeführt werden und wird als unsicher angesehen.Fazit
Wenn Ihre Methode die Klassenvariablen nicht ändert und niemals ändert, markieren Sie sie als const und stellen Sie sicher, dass alle erforderlichen Kriterien erfüllt sind. Dadurch kann mehr sauberer Code geschrieben werden, wodurch er konstant bleibt . Allerdings setzt
const
überall , ohne es zu jedem Gedanken sicherlich zu geben , ist nicht der Weg zu gehen.quelle
const
ignoriert . Es ist wichtig für Klassentypen. Bei Parametern, die als Wert übergeben werden, wird die oberste Ebene in Funktionsdeklarationen ignoriert. Es hat nur Bedeutung in der Definition.const
const
eine Funktion: Es ändert den Typ vonthis
, was istT const*
, anstattT*
. Alle anderen Effekte ergeben sich daraus. (Und es verhindert nicht jede Änderung des Objekts:mutable
Daten können immer noch geändert werden, und die Funktion kann const legal wegwerfen und alles ändern, was sie wünscht.)Es gibt wenig Wert bei der Zugabe
const
Qualifikationen nicht-reference / Nicht-Zeiger rvalues, und kein Punkt in Zugabe zu Einbauten.Im Fall von benutzerdefinierten Typen , eine
const
wird Qualifizierungs Anrufer verhindert eine nicht Aufrufconst
Memberfunktion auf dem Objekt zurückgegeben. Zum Beispiel gegebenconst std::string foo(); std::string bar();
dann
foo().resize(42);
wäre verboten, während
bar().resize(4711);
wäre erlaubt.
Für Einbauten wie
int
macht dies überhaupt keinen Sinn, da solche Werte sowieso nicht geändert werden können.(Ich erinnere mich jedoch an Effective C ++ , in dem es darum ging , den Rückgabetyp
operator=()
einerconst
Referenz zu erstellen, und dies ist zu berücksichtigen.)Bearbeiten:
Es scheint, dass Scott diesen Rat tatsächlich gegeben hat . Wenn ja, dann finde ich es aus den oben genannten Gründen selbst für C ++ 98 und C ++ 03 fraglich . Für C ++ 11 halte ich es eindeutig für falsch , wie Scott selbst herausgefunden zu haben scheint. In den Errata für Effective C ++, 3. Aufl. schreibt er (oder zitiert andere, die sich beschwert haben):
Und später:
quelle
Sie könnten den Punkt von Meyers Rat verpassen. Der wesentliche Unterschied besteht im
const
Modifikator für die Methode.Dies ist eine nicht konstante Methode (Hinweis Nr.
const
Am Ende), was bedeutet, dass der Status der Klasse geändert werden darf.int& operator[](const int index)
Dies ist eine const-Methode (Hinweis
const
am Ende)const int operator[](const int index) const
Was ist mit den Parametertypen und dem Rückgabewert? Es gibt einen geringfügigen Unterschied zwischen
int
undconst int
, der jedoch für den Hinweis nicht relevant ist. Was Sie beachten sollten, ist, dass die Nicht-Konstanten-Überladung zurückgegeben wird,int&
was bedeutet, dass Sie sie beispielsweise zuweisen könnennum[i]=0
, und dass die Konstanten-Überladung einen nicht modifizierbaren Wert zurückgibt (unabhängig davon, ob der Rückgabetypint
oder istconst int
).Meiner persönlichen Meinung nach , wenn ein Objekt übergeben wird durch Wert ,
const
Modifikator ist überflüssig. Diese Syntax ist kürzer und erreicht das gleicheint& operator[](int index); int operator[](int index) const;
quelle
const
Wertes.foo
Beispiel mit einemoperator[]
Beispiel. Er könnte den Punkt übersehen, an dem derconst
Modifikator für die Methode den Unterschied macht, nicht den Rückgabetyp.Der Hauptgrund für die Rückgabe von Werten als const ist, dass Sie so etwas nicht sagen können
foo() = 5;
. Dies ist eigentlich kein Problem bei primitiven Typen, da Sie keine Werte für primitive Typen zuweisen können, aber es ist ein Problem bei benutzerdefinierten Typen (z. B.(a + b) = c;
bei überladenen Typenoperator+
).Ich habe die Rechtfertigung dafür immer als ziemlich schwach empfunden. Sie können niemanden aufhalten, der beabsichtigt, umständlichen Code zu schreiben, und diese besondere Art von Zwang hat meiner Meinung nach keinen wirklichen Vorteil.
Mit C ++ 11 schadet diese Redewendung tatsächlich sehr : Die Rückgabe von Werten als const verhindert Verschiebungsoptimierungen und sollte daher nach Möglichkeit vermieden werden. Grundsätzlich würde ich dies jetzt als Anti-Muster betrachten.
Hier ist ein tangential verwandter Artikel zu C ++ 11.
quelle
Als dieses Buch geschrieben wurde, war der Rat von geringem Nutzen, diente jedoch dazu, den Benutzer beispielsweise daran zu hindern, zu schreiben
foo() = 42;
und zu erwarten , dass er etwas Beständiges ändert.Im Fall von
operator[]
kann dies etwas verwirrend sein, wenn Sie nicht auch eine Nichtüberladungconst
angeben, die eine Nichtreferenz zurückgibtconst
, obwohl Sie diese Verwirrung möglicherweise verhindern können, indem Sie eineconst
Referenz oder ein Proxyobjekt anstelle eines Werts zurückgeben.const
Heutzutage ist es ein schlechter Rat, da Sie das Ergebnis nicht an eine (Nicht- ) Wertreferenz binden können.(Wie in den Kommentaren ausgeführt, ist die Frage bei der Rückgabe eines primitiven Typs wie "
int
, da die Sprache Sie daran hindert, einemrvalue
solchen Typ zuzuweisen, umstritten. Ich spreche über den allgemeineren Fall, der die Rückgabe benutzerdefinierter Typen umfasst. )quelle
int
.foo() = 42;
ist illegal, unabhängig davon, ob der Rückgabetyp deklariert istint
oderint const
. Ich glaube nicht, dass Scott für die Verwendung vonconst
Stellen wie Rückgabetypen und Parametern in Funktionsdeklarationen argumentiert hat , wo sie vom Compiler ignoriert werden.Für primitive Typen (wie
int
) spielt die Konstanz des Ergebnisses keine Rolle. Bei Klassen kann sich das Verhalten ändern. Beispielsweise können Sie möglicherweise keine nicht konstante Methode für das Ergebnis Ihrer Funktion aufrufen:class Bigint { const C foo() const { ... } ... } Bigint b; b.foo().bar();
Das Obige ist verboten, wenn
bar()
es sich um eine const-Member-Funktion von handeltC
. Wählen Sie im Allgemeinen, was Sinn macht.quelle
Schau dir das an:
const int operator[](const int index) const
das
const
am Ende der Erklärung. Es beschreibt, dass diese Methode für Konstanten aufgerufen werden kann.In der anderen Hand, wenn Sie nur schreiben
int& operator[](const int index)
Es kann nur für Instanzen ohne Konstante aufgerufen werden und bietet außerdem:
big_int[0] = 10;
Syntax.
quelle
Eine Ihrer Überladungen besteht darin, einen Verweis auf ein Element im Array zurückzugeben, das Sie dann ändern können.
int& operator[](const int index) { return _data[index]; }
Die andere Überladung gibt einen Wert zurück, den Sie verwenden können.
const int operator[](const int index) const { return _data[index]; }
Da Sie jede dieser Überladungen auf dieselbe Weise aufrufen, wird der Wert bei Verwendung niemals geändert.
int foo = myBigInt[1]; // Doesn't change values inside the object. myBigInt[1] = 2; // Assigns a new value at index `
quelle
Im Beispiel des Array-Indexierungsoperators (
operator[]
) macht es einen Unterschied.Mit
int& operator[](const int index) { /* ... */ }
Sie können die Indizierung verwenden, um den Eintrag im Array direkt zu ändern, z. B. wie folgt:
mybigint[3] = 5;
Der zweite
const int operator[](const int)
Operator wird nur zum Abrufen des Werts verwendet.Als Rückgabewert von Funktionen spielt es jedoch für einfache Typen wie z. B.
int
keine Rolle. Wenn es darauf ankommt, geben Sie komplexere Typen zurück, z. B. astd::vector
, und möchten nicht, dass der Aufrufer der Funktion den Vektor ändert.quelle
Der
const
Rückgabetyp ist hier nicht so wichtig. Da int Provisorien nicht veränderbar sind, gibt es keine offensichtlichen Unterschiede im Umgang mitint
vsconst int
. Sie würden einen Unterschied sehen, wenn Sie ein komplexeres Objekt verwenden würden, das geändert werden könnte.quelle
Es gibt einige gute Antworten, die sich auf die Technik der beiden Versionen beziehen. Für einen primitiven Wert macht es keinen Unterschied.
Allerdings habe ich immer als
const
für den Programmierer , dort zu sein , anstatt die Compiler. Wenn Sie schreibenconst
, sagen Sie ausdrücklich "das sollte sich nicht ändern". Seien wir ehrlich,const
kann normalerweise sowieso umgangen werden, oder?Wenn Sie a zurückgeben
const
, teilen Sie dem Programmierer, der diese Funktion verwendet, mit, dass sich der Wert nicht ändern soll. Und wenn er es ändert, macht er wahrscheinlich etwas falsch, weil er / sie es nicht muss.EDIT: Ich denke auch, "
const
wann immer möglich verwenden" ist ein schlechter Rat. Sie sollten es dort verwenden, wo es Sinn macht.quelle