Amin Negm-Awad schrieb:
Ich stell mich nicht an. Ich würde gerne wissen was an << und >> für raus bzw. rein logischer sein soll als an << und >> für rein bzw. raus.
Ich glaube , dass dem eine ganz private Logik zugrunde liegt.
Nein, das ist eigentlich ziemlich einfach zu verstehen und auch sehr elegant umgesetzt.
Wenn man in C++ (wie auch in Swift) einen Infix-Operator wie << überlädt, tut man das für den linken Operanden. Eine Stream-Expression fängt in C++ also immer links mit dem Stream-Objekt an. Und der Operand arbeitet nicht nur auf einer ostream-Referenz, sondern er gibt sie auch wieder als Rückgabewert zurück, so daß man ihn konkatenieren kann, so oft man will.
Daß man etwas auf der Konsole ausgeben will ist nur der "zufällige" Sonderfall, daß das Stream-Objekt die vordefinierte ostream-Instanz cout ist und keine Datei, kein temporärer Puffer im RAM oder sonst irgendein Objekt, dessen Klasse von ostream abgeleitet ist.
Ganz konkret sieht eine Ausgabe in C++ also z.B. so aus:
Der Operator zeigt also völlig logisch, daß die rechten Operanden in genau der offensichtlichen Reihenfolge in den Stream reingeschoben werden.
Eine formatierte Ausgabe geht auch ganz einfach, nämlich so:
Man schmeißt einfach die Formatierungssteuerungs-Objekte mit in den Ausgabestrom und alles funktioniert automatisch richtig.
Und der Witz an der Sache ist, daß das mit allen Objekten aller ostream-abgeleiteten Klassen exakt genauso funktioniert – die Konsolen-Ausgabe ist nur ein Spezialfall, der eben keinen Extra-Befehl braucht ("print" – aber Moment mal, wo ist da eigentlich das Papier?), sondern man gibt einfach bloß in das ostream-Objekt aus, an dem zufällig die Konsole hängt.
Und analog funktioniert es für die istream-Klasse, nämlich so:
Auch hier zeigt der Pfeil wieder ganz offensichtlich, daß aus dem istream source Daten in die Variable line herausgezogen werden.
Völlig offensichtlich und intuitiv.
Und da C++ mehrfache Vererbung unterstützt, gibt es auch auf diesem Weg die Klasse iostream, deren Objekte gleichzeitig beides können (z.B. für I/O-Ports). (Das würde man in Objective-C und Swift auf anderem Weg lösen müssen.)
Solche Konstrukte sind in C++ sehr einfach, übersichtlich und elegant nutzbar, und Swift führt diese sprachliche Eleganz auf Compiler-Ebene jetzt eben mit der dynamischen Eleganz der Objective-C-Runtime zusammen in eine gemeinsame, konsistente Sprache.
In C++ war die Operator-Überladung übrigens gegenüber Swift noch relativ eingeschränkt: Es gab immer nur die vordefinierten Operatoren und innerhalb von Expressions war die Auswertungs-Priorität fest vorgegeben; Es galt also z.B. immer die Punkt-vor-Strich-Semantik, auch bei Überladung.
In Swift kann man nicht nur aus den für Operatoren zugelassenen Zeichen beliebige Operatoren definieren, sondern auch deren Auswertungs-Priorität beliebig festlegen.
Wie schon jemand zuvor gesagt hatte, habe auch ich noch nie ein Problem mit der Operator-Überladung erlebt (wobei ich die selber schon mehrfach genutzt habe), aber es ist natürlich ein sehr expressives Werkzeug, das erhöhte Ansprüche an die Konzeption und vor allem auch Dokumentation stellt.
Normale, benannte Funktionen und Methoden erklären sich aber eben auch nicht generell von selbst, gerade was ihre Semantik angeht.