Überblick 7. Überladen von Operatoren

Transcription

Überblick 7. Überladen von Operatoren
Überblick
1.
2.
Einführung C++ / Entwicklung/ Sprachfamilie
Nicht objektorientierte Erweiterungen von C
2.1 Das Ein-/Ausgabekonzept von C++
2.2 Referenzen in C++
2.3 Heap-Allokatoren in C++
3. Grundlagen des Typkonzepts von C++
4. Ziele der Objektorientierung
5. Objekt und Klasse, Elementfunktionen (Methoden)
6. Konstruktoren, Copy-Konstruktor, Destruktor und Wertzuweisung
7. Operatoren, Überladen von Operatoren
8. Elementobjekte und Elementobjektkonstruktoren
9. Klassenhierarchien und Vererbung
10. Polymorphie
11. Abstrakte Klassen
R.Grossmann / P. Sobe
1
7. Überladen von Operatoren
Betrachten wir noch einmal die Methode add
vektor add(vektor a) {return vektor(x+a.x,y+a.y,z+a.z);}
und ihre Nutzung
z.B. neu = n.add(a).add(b).add(d);
Jetzt möchte man statt dieser etwas umständlichen Notation die
übliche Schreibweise mit Infix-Operatoren benutzen:
neu = n + a + b + d;
Dies erreicht man mit dem Konzept des Überladens von
Operatoren. Dazu braucht man die obige Definition nur geringfügig
ändern:
vektor operator +(vektor a) {return vektor(x+a.x,y+a.y,z+a.z);}
R.Grossmann / P. Sobe
2
Überladen von Operatoren
Demonstration einer Klasse mit klassischen Methoden und mit
Operatoren zur Verdeutlichung des Unterschieds:
vektor_4.cpp und vektor_5.cpp
Probleme:
1. Man darf nur Operatoren überladen, die der Compiler
bereits für Standardtypen kennt.
Es können keinen neuen Symbole hinzugefügt werden.
Beispiel Kreuzprodukt von Vektoren:
Man hätte auf die Idee kommen können zu schreiben
vektor operator #(vektor a){return ....}
Das geht nicht, da # kein Operator ist, wie auch §, $ usw.
Deshalb wurde im Beispielprogramm der Modulo-Operator
% für das Kreuzprodukt verwendet.
R.Grossmann / P. Sobe
3
Überladen von Operatoren
Probleme (Fortsetzung):
2. Die Priorität des Operators bleibt laut Definition erhalten
und kann nicht verändert werden.
3. Man darf folgende Operatoren nicht überladen:
. .* :: ? :
(.* bedeutet Zugriff auf ein Element als Zeiger und
gleichzeitige Dereferenzierung, selten benutzt!)
4. Überladene binäre Operatoren als Elementfunktionen
dürfen nur einen Parameter besitzen, eine Definition wie
vektor add(float f,vektor a) {return
falsch !
vektor(f*a.x,f*a.y,f*a.z);}
ist verboten. Solche Überladungen sind nur als NichtElementfunktionen außerhalb der Klasse gestattet.
R.Grossmann / P. Sobe
4
Überladen von Operatoren
Beispiel: Überladung des <<-Operators in der Klasse ostream
Die Ausgabenotation ist bekannt.
int x; x=5; cout << x;
Der Compiler weiß, was er tun muss.
Wollen Sie aber schreiben:
vektor x(-2,3,5); cout << x;
dann weiß der Compiler nicht , wie er einen Vektor ausgeben
soll! Dann muss der <<-Operator überschrieben werden.
Dazu sind aber zwei Parameter nötig, da das cout-Objekt vom
Typ ostream ist, aber x vom Typ vektor.
R.Grossmann / P. Sobe
5
Überladen von Operatoren
Deshalb muss der <<-Operator als Nicht-Elementfunktion
außerhalb der Klasse überladen werden, zum Beispiel:
// Ausgabe eines Vektors in der Form ( x , y , z )
ostream& operator << (ostream& s, vektor v)
{ return s << ” (”<<v.i()<< ”, ”<<v.j()<< ”, ”<<v.k()<< ” )”;}
Beachten Sie dabei, dass Sie von außerhalb keinen direkten
Zugriff auf die privaten Daten innerhalb vektor v haben, also
auf x, y und z. Es müssen deshalb Methoden (hier i(), j() und
k()) geschrieben werden, die Zugriff auf die Komponenten des
vektor v zulassen.
Siehe dazu Programmcode vektor_6.cpp.
R.Grossmann / P. Sobe
6
Überladen von einstelligen Operatoren
Einstellige Operatoren können als Präfix- oder als PostfixOperatoren entworfen werden.
Beispiel:
Das Vorzeichen (-,+) einer komplexen Zahl als Präfix.
Präfix-Operatoren werden parameterlos überladen
complex operator -()
{ return complex(-real(),-imag() );}
complex a(-3,9),x; x = - a ;
Achtung: Im Beispiel wird der Wert der komplexen Zahl nicht
geändert. Es gibt auch Prefix-Operatoren, die den Wert des Objekts
ändern, z.B. -- zahl; für Integer-Werte. Dann unterscheidet sich ein
Prefix-Operator von einem Postfix-Operator, z.B. zahl--;
R.Grossmann / P. Sobe
7
Überladen von einstelligen Operatoren
Beispiel:
Der Inkrementierungsoperator ++ als Postfix-Operator.
Postfix-Operatoren werden mit Parameter int überladen.
“int“ ist dabei nur ein Dummy-Parameter, um eine Unterscheidung
zwischen Präfix-++ und Postfix-++ zu ermöglichen.
Im Beispiel vektor soll der Postfix-++ Operator den alten
Wert des vektor-Objektes übergeben und danach zum alten
Wert den Einheitsvektor addieren.
vektor operator ++( int )
{ vektor h=*this; ++x; ++y; ++z;
return h;
}
Die Bedeutung von *this wird auf den nächsten Folien erklärt!
R.Grossmann / P. Sobe
8
Übersicht (1)
Zweistellige Operatoren:
werden mit einem Parameter definiert, der den Typ des jeweiligen
zweiten Operanden (hier obj2) angibt.
Benutzung: erg = obj + obj2;
Definition am Beispiel vektor:
vektor operator +(vektor a) {return vektor(x+a.x,y+a.y,z+a.z);}
Einstellige Operatoren:
Prefix-Operatoren: ++x
… werden mit einer leeren Parameterliste definiert.
Postfix-Operatoren: x++
… werden mit einem Dummy-“int“ in der Parameterliste definiert.
R.Grossmann / P. Sobe
9
Übersicht (2)
Einstellige Prefix-Operatoren:
… werden mit einer leeren Parameterliste definiert.
Benutzung: erg = ++obj;
Definition am Beispiel vektor:
vektor operator ++( ) { ++x; ++y; ++z; return *this;}
Einstellige Postfix-Operatoren:
… werden mit einem Dummy-“int“ in der Parameterliste definiert.
Benutzung: erg = obj++;
Definition am Beispiel vektor:
vektor operator ++( int ) { vektor h=*this; ++x; ++y; ++z;
return h;
}
R.Grossmann / P. Sobe
10
Der *this - Zeiger
Nachdem wir den <<-Operator als Nicht-Elementfunktion außerhalb der
Klasse überladen haben, wollen wir
diesen gleich in der Elementfunktion drucke() so verwenden.
Da aber die Definition des Überladens
ostream& operator << (ostream& s, vektor v) ....
in der Klasse nicht bekannt ist, müssen wir drucke() auch außerhalb,
hinter der ostream& operator << -Definition plazieren:
void vektor::drucke(){ cout << “ \n “<< ??? ; }
An der Stelle ??? müsste eigentlich das auszugebende
Objekt stehen! Wie heißt aber das Objekt in der Klasse
selbst? Dafür wurde der this-Zeiger eingeführt. Durch
Dereferenzierung kommt man zum Wert des Objekts:
void vektor::drucke(){ cout << “ \n “<< *this ; }
R.Grossmann / P. Sobe
11
Der *this - Zeiger
Erklärung zu Programm vektor_7.cpp!
Da in jeder Klasse zwar die Datenstruktur (private-Teil) definiert ist, aber
das Objekt als Ganzes innerhalb der Klasse keinen Namen hat, wurde
dafür der this-Zeiger eingeführt.
Über *this kann man auf den Wert des Objektes zugreifen.
Der this-Zeiger kann auch bei Element-Funktionen eingesetzt werden, die
sonst den Typ void zurückbringen würden. Durch return *this; kann jetzt
der klassenspezifische Typ zurückgegeben werden.
Beispiel: Klasse complex
Sie haben eine Methode konj(), die den konjugiert komplexen Wert des
Objektes erzeugen soll und man kann in der Klasse complex definieren
void konj() {i=-i;} // i sei der Imaginärteil in der Klasse
R.Grossmann / P. Sobe
12
Der *this - Zeiger
Besser wäre die Nutzung des this-Zeigers , also:
statt
void konj() {i=-i;} kann man schreiben
complex konj() { i=-i; return *this; }
Das ist wesentlich eleganter, da jetzt die Methoden auf Objekte
mehrfach hintereinander ausgeführt werden können,
da sie den Klassentyp zurückbringen, was wir bereits bei den
Element-Funktionen add, sub,... erörtert haben !
Hinweis: Das Programm vektor_8.cpp implementiert einige
interessante Methoden, Einheitsvektor, Parallelität, Senkrechtstehen und die Gleichheit von Vektoren unter Nutzung des thisZeigers.
R.Grossmann / P. Sobe
13