2011-01-16 4 views
22

Ich lese ein Buch über C++ und genauer über das Überladen des Operators.C++ warum der Zuweisungsoperator eine const ref zurückgeben sollte, um zu vermeiden (a = b) = c

Das Beispiel ist die folgende:

const Array &Array::operator=(const Array &right) 
{ 
// check self-assignment 
// if not self- assignment do the copying 
return *this; //enables x=y=z 
} 

Die Erklärung durch das Buch bereitgestellt etwa const ref anstelle von ref zurückkehr Zuordnungen zu vermeiden, wie (x = y) = z. Ich verstehe nicht, warum wir das vermeiden sollten. Ich verstehe, dass x = y zuerst in diesem Beispiel ausgewertet wird und da es eine konstante Referenz zurückgibt, kann der = z-Teil nicht ausgeführt werden. Aber warum?

+3

Welches Buch? Das sieht für mich wie eine unnötige Vorsichtsmaßnahme aus. Ich kann mir nicht vorstellen, dass jemand '(x = y) = z' schreibt - warum sollten sie? Und ohne die Parens wird 'x = y = z 'als' x = (y = z) 'geparst, was vollkommen sinnvoll ist, also besteht dort kein Risiko. –

+1

Aber warum was? Warum ist es ein const ref? Warum wird es in dieser Reihenfolge ausgeführt? Warum kann 'z' nicht zugewiesen werden (x = y)? – Lazarus

+16

@antronis: Holen Sie sich ein besseres C++ Buch. – ybungalobill

Antwort

24

(x=y) bedeutet x.operator=(y), die das Objekt x zurückgibt. Daher bedeutet (x=y)=z(x.operator=(y)).operator=(z). Der Ausdruck in Parens setzt x auf y und gibt x zurück, und dann setzt das äußere Bit x auf z. Es setzt y nicht auf z, wie Sie vielleicht erwarten, und wie der Ausdruck x = y = z tut.

Dieses Verhalten ist kontraintuitiv (sie sollten nach der Zuweisung alle gleich sein, richtig?); die Rückgabe einer const-Referenz macht es unmöglich und vermeidet das Problem.

+2

Während das Verhalten kontraintuitiv klingt, ist es kein "Problem", weil es etwas ist, das der Programmierer absichtlich getan hätte (weshalb sie Klammern verwendet haben) – James

+18

Wer würde erwarten, dass '(x = y) = z' gesetzt wird 'y' zu' z'? Dies ist ein sehr künstliches "Problem" und ein C++ Buch sollte keine Zeit damit verbringen, Vorsichtsmaßnahmen dagegen zu erklären. '(x = 5) = z 'setzt' 5' nicht auf 'z'. –

+0

Wenn Sie erwarten, dass die Zuordnung assoziativ ist (wie beim Vergleich), würden Sie erwarten, dass alle drei am Ende gleich sind. Naiv, aber eine vernünftige Interpretation auf den ersten Blick. Ich stimme zu, dass dieses Formular nicht allgemein verwendet werden sollte und nicht in dem Buch hervorgehoben werden sollte, aber es ist gut, dem Compiler zu helfen, sich nicht in den Fuß zu schießen, wenn möglich *) –

18

Es gibt keine Notwendigkeit, dies zu vermeiden, es sei denn, das Buch richtet sich an Programmierer, die normalerweise (x=y)=z schreiben, wenn sie x=y=z bedeuten. In der Praxis schreibt das niemand richtig, also ist die Vorsorge völlig unnötig. Es verbietet auch einige andere knappe Konstrukte wie (x=y).nonConstMember(), die kaum jemand schreibt, die aber in einigen Kontexten nützlich sein könnten (obwohl sie nicht überbeansprucht werden sollten).

@ybungalobill ist richtig, erhalten Sie ein besseres Buch.

+9

+1. Ein Programmierer, der versehentlich "(x = y) = z" schreibt, scheint ungefähr so ​​wahrscheinlich (und wie notwendig, um davor zu wachen), wenn ein Programmierer versehentlich 'x = y + system (" rm -rf/") schreibt, wenn sie gemeint haben 'x = y'. –

+0

@j_random_hacker klingt möglich in einem "Hoppla, falsches Fenster!" Szenario ... – Calimo

+1

Aber ich habe gesehen 'if ((x = y) = z) ...' wenn der Autor meinte 'if ((x = y) == z) ... '. –

10

Soweit ich weiß, geben Zuweisungsoperatoren keine Const-Referenzen in idiomatischem C++ zurück. Die Standardtypen geben auch keine const-Referenzen zurück.

std::string a, b, c; 
(a = b).clear(); // no objection from compiler 

Alle meine benutzerdefinierten Zuweisungsoperatoren haben eine nicht konstante Referenz zurückgegeben.

Überprüfen Sie im Zweifelsfall die Standardbibliothek. Es ist nicht makellos, aber grundlegende Dinge wie dieses werden auf jeden Fall korrekt.

+0

Die Boost-Leute geben auch häufig änderbare Refs zurück. –

3

Ich würde auf das Verhalten der eingebauten Typen aussehen.

Bei der Definition eigener Typen ist es vorzuziehen, dass sich die Operatoren genauso verhalten wie die integrierten Typen. Dies ermöglicht eine einfache Übernahme Ihrer Klassen, ohne dass Sie sich in Ihren Code einarbeiten müssen, um zu sehen, warum sie sich anders verhalten als erwartet. So

wenn wir ganze Zahlen sehen:

int main() 
{ 
    int x = 5; 
    int y = 6; 
    int z = 7; 

    (x = y) = z; 
    std::cout << x << " " << y << " " << z << "\n"; 
} 

Dies funktioniert mit y unverändert und x 7. In Ihrem Code zugewiesen wird, ich würde erwarten, dass Ihr Zuweisungsoperator die gleiche Art und Weise zu arbeiten. Die Standard-Zuweisungsoperator Definition:

Array& Array::operator=(Array const& rhs) 
{ 
    /* STUFF */ 
    return *this; 
} 

das tun sollte gut (unter der Annahme/* STUFF */ist korrekt).

1

Der einzige Grund, den ich sehen kann, ist, dass dieses Buch geschrieben wurde, um C++ zu C-Programmierern zu erklären (oder von einem Autor, dessen C Verständnis besser als C++ versteht). Da für einen C-Programmierer der Ausdruck (x = y) = z für integrierte Typen ungültig ist, wird er wahrscheinlich versuchen, dasselbe Verhalten mit seinen benutzerdefinierten Typen zu erhalten.

Allerdings sind C und C++ verschiedene Sprachen, und in C++ ist der Ausdruck (x = y) = z auch für integrierte Typen gültig. Wenn Sie also dasselbe Verhalten für Ihre benutzerdefinierten Typen haben möchten, sollten Sie eine nicht konstante Referenz in operator = zurückgeben.

Ich würde Ihnen raten, ein besseres Buch zu bekommen, eines, das nicht die Verwirrung zwischen C und C++ macht. Sie sind nicht die gleichen Sprachen, auch wenn sie von einer gemeinsamen Basis abstammen.

Verwandte Themen