In vielen Beiträgen wird über die Überlastung der Bediener geklagt.
Ich hatte das Gefühl, dass ich die Konzepte des "Operator Overloading" klären musste, indem ich einen alternativen Blickwinkel auf dieses Konzept anbiete.
Code-Verschleierung?
Dieses Argument ist ein Trugschluss.
Obfuscating ist in allen Sprachen möglich...
Es ist genauso einfach, Code in C oder Java durch Funktionen/Methoden zu verschleiern wie in C++ durch Operatorüberladungen:
// C++
T operator + (const T & a, const T & b) // add ?
{
T c ;
c.value = a.value - b.value ; // subtract !!!
return c ;
}
// Java
static T add (T a, T b) // add ?
{
T c = new T() ;
c.value = a.value - b.value ; // subtract !!!
return c ;
}
/* C */
T add (T a, T b) /* add ? */
{
T c ;
c.value = a.value - b.value ; /* subtract !!! */
return c ;
}
...Selbst in den Standardschnittstellen von Java
Ein weiteres Beispiel ist die Cloneable
Schnittstelle in Java:
Sie sollen das Objekt, das diese Schnittstelle implementiert, klonen. Aber Sie könnten lügen. Und ein anderes Objekt erstellen. Tatsächlich ist diese Schnittstelle so schwach, dass Sie einen anderen Objekttyp zurückgeben könnten, nur so zum Spaß:
class MySincereHandShake implements Cloneable
{
public Object clone()
{
return new MyVengefulKickInYourHead() ;
}
}
Da die Cloneable
Schnittstelle missbraucht/verschleiert werden kann, sollte sie aus denselben Gründen verboten werden, aus denen die Überladung von C++-Operatoren verboten werden soll?
Wir könnten die Überlastung der toString()
Methode eines MyComplexNumber
Klasse, um die Stunde des Tages in Form einer Zeichenkette zurückzugeben. Sollte die toString()
Überlastung auch verboten werden? Wir könnten sabotieren MyComplexNumber.equals
einen Zufallswert zurückgeben, die Operanden ändern usw. usw. usw..
In Java, wie auch in C++ oder einer anderen Sprache, muss der Programmierer beim Schreiben von Code ein Minimum an Semantik beachten. Das bedeutet die Implementierung einer add
Funktion, die addiert, und Cloneable
Implementierungsmethode, die klont, und eine ++
Operator als Inkremente.
Was ist überhaupt eine Verschleierung?
Da wir nun wissen, dass Code sogar durch die makellosen Java-Methoden sabotiert werden kann, können wir uns die Frage nach dem wirklichen Nutzen der Operatorüberladung in C++ stellen?
Klare und natürliche Notation: Methoden vs. Operatorüberladung?
Im Folgenden werden wir für verschiedene Fälle den "gleichen" Code in Java und C++ vergleichen, um eine Vorstellung davon zu bekommen, welcher Kodierungsstil klarer ist.
Natürliche Vergleiche:
// C++ comparison for built-ins and user-defined types
bool isEqual = A == B ;
bool isNotEqual = A != B ;
bool isLesser = A < B ;
bool isLesserOrEqual = A <= B ;
// Java comparison for user-defined types
boolean isEqual = A.equals(B) ;
boolean isNotEqual = ! A.equals(B) ;
boolean isLesser = A.comparesTo(B) < 0 ;
boolean isLesserOrEqual = A.comparesTo(B) <= 0 ;
Bitte beachten Sie, dass A und B in C++ von beliebigem Typ sein können, solange die Operatorüberladungen vorhanden sind. Wenn A und B in Java keine Primitive sind, kann der Code sehr verwirrend werden, selbst für primitiv-ähnliche Objekte (BigInteger usw.)...
Natürliche Array/Container-Zugriffsmöglichkeiten und Subscripting:
// C++ container accessors, more natural
value = myArray[25] ; // subscript operator
value = myVector[25] ; // subscript operator
value = myString[25] ; // subscript operator
value = myMap["25"] ; // subscript operator
myArray[25] = value ; // subscript operator
myVector[25] = value ; // subscript operator
myString[25] = value ; // subscript operator
myMap["25"] = value ; // subscript operator
// Java container accessors, each one has its special notation
value = myArray[25] ; // subscript operator
value = myVector.get(25) ; // method get
value = myString.charAt(25) ; // method charAt
value = myMap.get("25") ; // method get
myArray[25] = value ; // subscript operator
myVector.set(25, value) ; // method set
myMap.put("25", value) ; // method put
In Java gibt es für jeden Container, der das Gleiche tun soll (Zugriff auf seinen Inhalt über einen Index oder Bezeichner), eine andere Methode, was verwirrend ist.
In C++ verwendet jeder Container dank der Überladung durch Operatoren denselben Weg, um auf seinen Inhalt zuzugreifen.
Natürliche fortgeschrittene Typen Manipulation
Die folgenden Beispiele verwenden eine Matrix
Objekt, das über die ersten Links gefunden wurde, die bei Google für " Java-Matrix-Objekt " und " C++ Matrix-Objekt ":
// C++ YMatrix matrix implementation on CodeProject
// http://www.codeproject.com/KB/architecture/ymatrix.aspx
// A, B, C, D, E, F are Matrix objects;
E = A * (B / 2) ;
E += (A - B) * (C + D) ;
F = E ; // deep copy of the matrix
// Java JAMA matrix implementation (seriously...)
// http://math.nist.gov/javanumerics/jama/doc/
// A, B, C, D, E, F are Matrix objects;
E = A.times(B.times(0.5)) ;
E.plusEquals(A.minus(B).times(C.plus(D))) ;
F = E.copy() ; // deep copy of the matrix
Und dies ist nicht auf Matrizen beschränkt. Die BigInteger
y BigDecimal
Klassen in Java leiden unter der gleichen verwirrenden Ausführlichkeit, während ihre Entsprechungen in C++ so klar sind wie eingebaute Typen.
Natürliche Iteratoren:
// C++ Random Access iterators
++it ; // move to the next item
--it ; // move to the previous item
it += 5 ; // move to the next 5th item (random access)
value = *it ; // gets the value of the current item
*it = 3.1415 ; // sets the value 3.1415 to the current item
(*it).foo() ; // call method foo() of the current item
// Java ListIterator<E> "bi-directional" iterators
value = it.next() ; // move to the next item & return the value
value = it.previous() ; // move to the previous item & return the value
it.set(3.1415) ; // sets the value 3.1415 to the current item
Natürliche Funktoren:
// C++ Functors
myFunctorObject("Hello World", 42) ;
// Java Functors ???
myFunctorObject.execute("Hello World", 42) ;
Textverkettung:
// C++ stream handling (with the << operator)
stringStream << "Hello " << 25 << " World" ;
fileStream << "Hello " << 25 << " World" ;
outputStream << "Hello " << 25 << " World" ;
networkStream << "Hello " << 25 << " World" ;
anythingThatOverloadsShiftOperator << "Hello " << 25 << " World" ;
// Java concatenation
myStringBuffer.append("Hello ").append(25).append(" World") ;
- Ok, in Java können Sie verwenden
MyString = "Hello " + 25 + " World" ;
auch... Aber, Moment mal: Das ist doch eine Überlastung des Operators, oder? Ist das nicht Betrug???
-D
Generischer Code?
Derselbe generische Code, der Operanden ändert, sollte sowohl für Built-Ins/Primitives (die in Java keine Schnittstellen haben), Standardobjekte (die möglicherweise nicht die richtige Schnittstelle haben) als auch für benutzerdefinierte Objekte verwendet werden können.
Zum Beispiel die Berechnung des Durchschnittswerts von zwei Werten beliebigen Typs:
// C++ primitive/advanced types
template<typename T>
T getAverage(const T & p_lhs, const T & p_rhs)
{
return (p_lhs + p_rhs) / 2 ;
}
int intValue = getAverage(25, 42) ;
double doubleValue = getAverage(25.25, 42.42) ;
complex complexValue = getAverage(cA, cB) ; // cA, cB are complex
Matrix matrixValue = getAverage(mA, mB) ; // mA, mB are Matrix
// Java primitive/advanced types
// It won't really work in Java, even with generics. Sorry.
Diskussion über das Überladen von Operatoren
Nachdem wir nun einen fairen Vergleich zwischen C++-Code, der Operatorüberladung verwendet, und demselben Code in Java gesehen haben, können wir nun das Konzept der "Operatorüberladung" diskutieren.
Die Überlastung von Operatoren gab es schon vor dem Computer
*Auch außerhalb der Informatik gibt es die Überlastung von Operatoren: In der Mathematik gibt es zum Beispiel Operatoren wie +
, -
, `` usw. sind überlastet.**
In der Tat, die Bedeutung von +
, -
, *
usw. ändert sich je nach Art der Operanden (Zahlen, Vektoren, Quantenwellenfunktionen, Matrizen usw.).
Die meisten von uns haben im Rahmen ihrer naturwissenschaftlichen Ausbildung mehrere Bedeutungen für Operatoren gelernt, die von den Typen der Operanden abhängen. Fanden wir sie also verwirrend?
Die Überladung von Operatoren hängt von ihren Operanden ab
Dies ist der wichtigste Teil der Operator-Überladung: Wie in der Mathematik oder in der Physik hängt die Operation von den Typen ihrer Operanden ab.
Wenn Sie also den Typ des Operanden kennen, wissen Sie auch, welche Auswirkungen die Operation hat.
Sogar C und Java haben (fest kodierte) Operatorüberladung
In C ändert sich das tatsächliche Verhalten eines Operators je nach seinen Operanden. Die Addition zweier ganzer Zahlen unterscheidet sich beispielsweise von der Addition zweier Paschas oder sogar einer ganzen Zahl und eines Paschas. Es gibt sogar den ganzen Bereich der Zeigerarithmetik (ohne Casting kann man zu einem Zeiger eine ganze Zahl addieren, aber man kann nicht zwei Zeiger addieren...).
In Java gibt es keine Zeigerarithmetik, aber jemand hat die Stringverkettung ohne die +
Operator wäre lächerlich genug, um eine Ausnahme im Credo "Operatorüberladung ist böse" zu rechtfertigen.
Es ist nur so, dass Sie als C- (aus historischen Gründen) oder Java- (aus persönliche Gründe (siehe unten), können Sie keinen eigenen Kodierer bereitstellen.
In C++ ist das Überladen von Operatoren nicht optional...
In C++ ist das Überladen von Operatoren für eingebaute Typen nicht möglich (und das ist auch gut so), aber benutzerdefiniert Typen können haben benutzerdefiniert Operator-Überlastungen.
Wie bereits erwähnt, werden in C++, im Gegensatz zu Java, Benutzertypen nicht als Bürger zweiter Klasse der Sprache betrachtet, wenn sie mit eingebauten Typen verglichen werden. Wenn also eingebaute Typen über Operatoren verfügen, sollten auch Benutzertypen in der Lage sein, sie zu verwenden.
Die Wahrheit ist, dass, wie die toString()
, clone()
, equals()
Methoden sind für Java ( d.h. quasi-standardmäßig ), ist die Überladung von C++-Operatoren so sehr Teil von C++, dass sie so selbstverständlich wird wie die ursprünglichen C-Operatoren oder die zuvor erwähnten Java-Methoden.
In Verbindung mit der Schablonenprogrammierung wird das Überladen von Operatoren zu einem bekannten Entwurfsmuster. Tatsächlich kommt man in der STL nicht sehr weit, ohne überladene Operatoren und überladende Operatoren für die eigene Klasse zu verwenden.
...aber es sollte nicht missbraucht werden
Bei der Überladung von Operatoren sollte die Semantik des Operators beachtet werden. Subtrahieren Sie nicht in einer +
Operator (wie in "nicht subtrahieren in einer add
Funktion" oder "Rückgabe-Mist in einer clone
Methode").
Cast-Überladungen können sehr gefährlich sein, da sie zu Mehrdeutigkeiten führen können. Sie sollten daher wirklich nur für genau definierte Fälle verwendet werden. Was die &&
y ||
Überlasten Sie sie niemals, es sei denn, Sie wissen wirklich, was Sie tun, da Sie sonst die Kurzschlussauswertung verlieren, die die einheimischen Betreiber &&
y ||
genießen.
Also... Ok... Warum ist es dann in Java nicht möglich?
Weil James Gosling es gesagt hat:
Ich habe das Überladen von Operatoren als eine ziemlich persönliche Entscheidung weil ich gesehen hatte, dass zu viele Leute sie in C++ missbrauchen.
_James Gosling. Quelle: http://www.gotw.ca/publications/c_family_interview.htm_
Bitte vergleichen Sie den Text von Gosling oben mit dem von Stroustrup unten:
Viele C++-Entwurfsentscheidungen haben ihre Wurzeln in meiner Abneigung, Menschen zu zwingen, Dinge auf eine bestimmte Weise zu tun [...] Oft war ich versucht, eine Funktion zu verbieten, die ich persönlich nicht mochte, aber ich habe davon abgesehen, weil Ich habe nicht geglaubt, dass ich das Recht habe, anderen meine Ansichten aufzuzwingen. .
Bjarne Stroustrup. Quelle: Der Entwurf und die Entwicklung von C++ (1.3 Allgemeiner Hintergrund)
Würde die Überladung von Operatoren Java nützen?
Einige Objekte würden von der Überladung mit Operatoren sehr profitieren (konkrete oder numerische Typen, wie BigDecimal, komplexe Zahlen, Matrizen, Container, Iteratoren, Komparatoren, Parser usw.).
In C++ können Sie von diesem Vorteil profitieren, weil Stroustrup so bescheiden ist. In Java ist man einfach aufgeschmissen, weil Goslings persönliche Wahl .
Könnte es zu Java hinzugefügt werden?
Die Gründe dafür, dass das Operator-Overloading jetzt nicht in Java hinzugefügt wird, könnten eine Mischung aus interner Politik, Allergie gegen das Feature, Misstrauen gegenüber Entwicklern (Sie wissen schon, die Saboteure, die Java-Teams heimzusuchen scheinen...), Kompatibilität mit den früheren JVMs, Zeit, um eine korrekte Spezifikation zu schreiben, usw. sein.
Warten Sie also nicht zu lange auf diese Funktion...
Aber sie tun es in C#!!!
Ja...
Dies ist zwar bei weitem nicht der einzige Unterschied zwischen den beiden Sprachen, aber ich finde ihn immer wieder amüsant.
Offensichtlich haben die C#-Leute mit ihrem "Jedes Primitivum ist ein struct
und eine struct
leitet sich von Object" ab hat es beim ersten Versuch richtig gemacht.
Trotz all des FUD gegen das Überladen von definierten Operatoren unterstützen die folgenden Sprachen dies: Kotlin , Scala , Dart , Python , F# , C# , D , Algol 68 , Smalltalk , Groovy , Raku (früher Perl 6) , C++, Ruby , Haskell , MATLAB , Eiffel , Lua , Clojure , Fortran 90 , Schnell , Ada , Delphi 2005 ...
So viele Sprachen, mit so vielen unterschiedlichen (und manchmal gegensätzlichen) Philosophien, und doch sind sie sich in diesem Punkt alle einig.
Ein Denkanstoß...
6 Stimmen
Kann auch lesen Warum Java das Überladen von Operatoren nicht unterstützt
1 Stimmen
@zzzz, es fällt mir schwer, diesen Artikel zu lesen. Wurde er automatisch übersetzt, oder ist Englisch die zweite Sprache des Autors? Ich finde die Diskussion hier viel sauberer.
48 Stimmen
An die Leute, die dies als nicht konstruktiv abtun: Diese Frage hat einen der konstruktivsten Dialoge hervorgebracht, die ich bei SO gesehen habe. Vielleicht ist es ein besserer Kandidat für programmers.stackexchange.com aber es gibt Zeiten, in denen ich denke, dass SO übermäßig abweisend gegenüber breiteren Themen ist.
1 Stimmen
@NoNaMe es ist ganz einfach, einfach geistig einfügen a y die - Fehlende Artikel sind ein deutliches Zeichen dafür, dass die Person entweder kein englischer Muttersprachler oder ein Programmierer ist (oder wie dieser Typ, beides :) Der Grund, warum Programmierer Artikel weglassen können, ist, dass dadurch Kommentare kürzer werden und leichter in den zur Verfügung gestellten Platz passen von da an gewöhnen sie sich einfach daran. Mein Problem ist das Layout, irgendwie stoße ich bei der Google-Suche immer auf diese Seite. Zum Glück gibt es eine tolle Chrome-Erweiterung namens Eindeutig die schwer zu lesende Seiten wunderbar neu formatiert.
1 Stimmen
Ich sehe keinen Grund, warum und wie OP die erste Antwort akzeptiert hat? Antwort geschrieben von @ stackoverflow.com/benutzer/14089/paercebal ist ausgezeichnet. Sie sollte akzeptiert werden.
0 Stimmen
Ich wünschte, die
==
Operator würde für Zeichenketten überladen werden. Es erwischt mich jedes Mal, wenn ich von GO, PHP, RUBY, Python, .... zurück wechsle.