Lektion 55:

Operatoren außerhalb von Klassen

Die Operatoren, die wir bisher überladen haben, waren immer innerhalb einer Klasse. In unserem Beispiel innerhalb der Klasse Rational. Dabei ist der linke Operand das Objekt der Klasse, auf das der Operator aufgerufen wird, und der rechte Operand ist der, welcher der operator-Funktion übergeben wird.

 Das muß aber nicht so sein. Man kann auch Operatoren überladen, indem man die operator-Funktion nicht innerhalb der Klasse schreibt, sondern außerhalb. Dann gibt es keinen linken Operanden mehr, der automatisch eingesetzt werden kann. Deshalb haben solche Funktionen dann zwei Parameter.

Rational operator*(Rational links,Rational rechts)

 Dieses Verfahren muß allgemein immer dann angewandt werden, wenn die Klasse, die als linker Operand in Frage kommt, nicht verändert werden kann. D.h. wenn keine zusätzliche Methode in die Klasse eingefügt werden kann. Ein solcher Fall ist das Überladen des operator<< für cout. Die Klasse, zu der das Objekt cout gehört, können wir nicht verändern. Aber wir können trotzdem einen Operator schreiben, der als linken Operanden ein Objekt dieser Klasse verarbeiten kann. Das Objekt cout gehört (unter anderem) zur Klasse ostream. Damit würde der Operator ungefähr so aussehen:

void operator<<(ostream out,Rational r)

Beachten Sie, daß der Funktionsparameter nur out heißt, und nicht cout. Beim Aufruf wird dann cout genommen und eine Kopie davon angelegt, die in die Variable out gelegt wird.

Es soll dasselbe Objekt zurückgegeben werden, das erhalten wurde (nicht das gleiche Objekt). Dazu lassen sich Referenz-Parameter verwenden.

void operator<<(ostream &out,Rational r)
{
     cout<<r.m_zaehler<<'/'<<r.m_nenner;
};

Jetzt liegt aber dieser Operator außerhalb der Klasse Rational, und hat deshalb keinen Zugriff mehr auf die Attribute m_zaehler und m_nenner. Zu diesem Zweck lockern wir für kurze Zeit diese Zugriffsbeschränkung, indem ausnahmsweise die Attribute der Klasse Rational public sein sollen.

class Rational
{
public:
//usw.

Wir testen den neuen Operator mit folgender Funktion:

void main()
{
   Rational a(3,4);

   cout<<"a=";
   cout<<a;
   cout<<endl;
};

Es funktioniert! Aber daß sich der operator<< wirklich so verhält, wie wir es gewohnt sind, müßte auch folgendes funktionieren:

void main()
{
   Rational a(3,4);
   cout<<"a="<<a<<endl;
};

 Das funktioniert leider nicht. Um zu verstehen, warum das nicht funktioniert, müssen wir zunächst verstehen, wie es funktioniert, daß das Hintereinanderschreiben von Sachen an cout bewirkt, daß diese Sachen in der richtigen Reihenfolge auf den Bildschirm ausgegeben werden. Daß es geht, habe ich bereits in der zweiten Lektion erwähnt, und seitdem wurde es oft verwendet. Aber jetzt sind wir endlich so weit, diese Zeile zu verstehen:

Der Ausdruck

cout<<"a="<<a<<endl

lautet vollständig geklammert

((cout<<"a=")<<a)<<endl

Auf des Ergebnis des ersten Operators (der als Seiteneffekt etwas auf den Bildschirm ausgibt) wird der zweite Operator angewandt (der ebenfalls etwas ausgibt). Es muß also nur dafür gesorgt werden, daß das Ergebnis jedes operators wieder der linke Operand sein wird.

Damit geschieht bei der Auswertung folgendes:

((cout<<"a=")<<a)<<endl
Anwendung des ersten Operators
Wert: cout      Seiteneffekt: Ausgabe "a="
(cout<<a)<<endl
Anwendung des zweiten Operators
Wert: cout      Seiteneffekt: Ausgabe der Zahl
cout<<endl
Anwendung des dritten Operators
Wert: cout      Seiteneffekt: Ausgabe eines Zeilenwechsels

Um das richtige Verhalten zu bewirken, muß also dasselbe Objekt zurückgegeben werden, das als linker Operator übergeben wurde.

Es reicht wieder nicht, nur eine Kopie zurückzugeben. Die Seiteneffekte haben auch das Objekt cout (oder was immer übergeben wurde) verändert und deshalb soll wirklich dasselbe Objekt zurückgegeben werden. Also wird eine Referenz zurückgegeben.

ostream &operator<<(ostream &out,Rational r)
{
     cout<<r.m_zaehler<<'/'<<r.m_nenner;
     return cout;
};
void main()
{
   Rational a(1,2);
   Rational b(3,4);
   Rational c(5,6);
   Rational d(7,8);

   a=a+5;
   b=5+a;
   c=a*b;
   d=a+b;

   cout<<"a="<<a<<endl;
   cout<<"b="<<b<<endl;
   cout<<"c="<<c<<endl;
   cout<<"d="<<c<<endl;
};


Falls Ihnen Fehler im Text auffallen oder Sie Verbesserungsvorschläge haben, dann schicken Sie mir bitte eine Mail. Ich werde mich dann sofort darum kümmern.
[aktuelle Version] [inhalt] [index]      [Fehlerkorrektur, Verbesserungsvorschlag]

© Volkard Henkel <volkard@normannia.de>, last update: 08/25/00