Einleitung Überladen binärer arithmetischer Operatoren Weitere Operatoren Informationsverarbeitung Einführung in das Programmieren mit C++ Markus Uhlmann Institut für Hydromechanik Karlsruher Institut für Technologie www.ifh.kit.edu WS 2011/2012 Vorlesung 12 (Download Quelldateien) 1/24 Einleitung Überladen binärer arithmetischer Operatoren Weitere Operatoren 12. VORLESUNG Überladen von Operatoren Advice: Define operators primarily to mimic conventional usage. – B. Stroustrup 2/24 Einleitung Überladen binärer arithmetischer Operatoren Weitere Operatoren Fragen, die in dieser Vorlesung diskutiert werden I Wie können Operationen zwischen Objekten durch “natürliche” Operatorsymbole ausgedrückt werden? I Welche Möglichkeiten und Beschränkungen bestehen dabei? 3/24 Einleitung Überladen binärer arithmetischer Operatoren Weitere Operatoren Wieso Überladen von Operatoren? Beispiel: Arbeiten mit Vektoren I Addition von Vektoren: Addieren jedes Elementes int ort[3], verschiebung[3], resultat[3]; for (int i=0; i<3; i++) resultat[i]=ort[i]+verschiebung[i];//Addition per Element I implementiert durch eine Klasse “vektor”: vektor ort, verschiebung, resultat; resultat=ort.add(verschiebung); //Addition von Objekten I wäre Folgendes nicht praktisch? resultat=ort+verschiebung; //Addition von Objekten ⇒ Operator “+” überladen durch Additionsfunktion für Klasse “vektor” 4/24 Einleitung Überladen binärer arithmetischer Operatoren Weitere Operatoren Überladen von Operatoren: wie funktioniert es? resultat = ort + verschiebung; //Addition von Objekten typische Notation für Addition → verbirgt den Mechanismus (Details), betont das Essentielle I I Syntax für Operatorfunktionen: <rueckgabeTyp> operator <op> (<parameterListe>) I Compiler ersetzt I durch resultat=ort.operator+(verschiebung); <op> beinhaltet i.d.R. Sonderzeichen (hier erlaubt) resultat=ort+verschiebung; 5/24 Einleitung Überladen binärer arithmetischer Operatoren Weitere Operatoren Operator + Beschränkungen Operatoren -, * Einführung in überladene Operatoren an einem Beispiel Eine Klasse zur Behandlung von Zeitdaten Zeitangabe bestehend aus Stunden, Minuten I Wertebereich der Minuten: [0, 59] → 2 Ganzzahlen I Funktionen: Anzeigen, Summieren, Initialisieren, . . . I 12 13 14 15 16 17 18 19 20 21 22 23 class Time{ private: int hours; int minutes; public: Time(); Time(int h, int m=0); void AddMin(int m); void AddHr(int h); void Reset(int h=0, int m=0); Time Sum(const Time t) const; void Show() const; Version: ohne überladene Ops. 27 28 29 44 45 Time vorbereitung(1,30); Time implementierung(1,10); Time fehlersuche(0,40); gesamt=vorbereitung.Sum(implementierung); gesamt=gesamt.Sum(fehlersuche); (Quelltext 12.1) 6/24 Einleitung Überladen binärer arithmetischer Operatoren Weitere Operatoren Operator + Beschränkungen Operatoren -, * Version mit überladenem Additionsoperator Ersetzen der Funktion “Sum” durch Operator “+” ⇒ ermöglicht Operator- oder Funktionsschreibweise I 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Time{ private: int hours; int minutes; public: Time(); Time(int h, int m=0); void AddMin(int m); void AddHr(int h); void Reset(int h=0, int m=0); Time operator+(const Time t) const; void Show() const; }; Version: mit überladenem “+” 27 28 29 30 43 44 45 46 47 Time Time Time Time vorbereitung(1,30); implementierung(1,10); fehlersuche(0,40); gesamt; gesamt=vorbereitung+implementierung; gesamt=gesamt+fehlersuche; /* funktioniert auch: gesamt=gesamt.operator+(fehlersuche); */ (Quelltext 12.2) 7/24 Einleitung Überladen binärer arithmetischer Operatoren Weitere Operatoren Operator + Beschränkungen Operatoren -, * Regeln des Überladens von Operatoren (i) Regel 1: nur existierende Operatorsymbole verwenden es ist nicht möglich neue Operatorsymbole zu definieren B Bsp: Operator “**” zur Potenzierung (nicht möglich) I Regel 2: manche Operatoren können nicht überladen werden Operatorsymbole, die nicht zu überladen sind: B “sizeof”, “.”, “::”, “?” (und weitere) I die meisten Operatorsymbole sind überladbar (sh. Tabelle) I 8/24 Einleitung Überladen binärer arithmetischer Operatoren Weitere Operatoren Operator + Beschränkungen Operatoren -, * Regeln des Überladens von Operatoren (ii) Regel 3: mind. ein Operand muss benutzerdefiniert sein Überladen von Operatoren soll neuen Datentypen dienen I soll verhindern, Standardoperationen neu zu definieren B Bsp: Redefinition von “-” für zwei Ganzzahlen nicht möglich I Regel 4: Eigenschaften des Operators bleiben unverändert Überladen kann nicht die Priorität des Operators ändern ebenso bleibt Anzahl der Operanden erhalten B Bsp: I I int x=5; Time training; % x; //illegal fuer Restwertoperator % training; //illegal fuer ueberladenen Op. 9/24 Einleitung Überladen binärer arithmetischer Operatoren Weitere Operatoren Operator + Beschränkungen Operatoren -, * Regeln des Überladens von Operatoren (iii) Regel 5: einige Operatoren müssen Elementfunktionen sein die meisten Operatoren können entweder als Elementfunktionen oder nicht-Elementfunktionen implementiert werden B Ausnahmen sind: “=”, “()”, “[]”, “->” ⇒ nur als Elementfunktionen zu redefinieren I Empfehlung: Überladen sinnverwandt mit Originaloperator Operatorsymbol soll Sinn der Operation suggerieren I bei Nichtbeachtung: unverständlicher Code B Bsp: Überladen von “*” durch Vektoraddition (nein!) I 10/24 Einleitung Überladen binärer arithmetischer Operatoren Weitere Operatoren Operator + Beschränkungen Operatoren -, * Zeitklasse mit weiteren Operatoren (Version 1) I I 11 12 13 14 15 16 17 18 19 20 21 22 23 24 Funktion für Subtraktion zweier Zeitspannen: Multiplikation einer Zeitspanne mit Faktor: class Time{ private: int hours; int minutes; public: Time(); Time(int h, int m=0); Time operator+(const Time t) const; Time operator-(const Time t) const; Time operator*(const float mult) const; void Show() const; “-” “*” Version: mit +, -, * 27 28 29 30 42 43 44 int main(){ Time planen(18,30); Time herstellen(60,0); Time gesamt,diff,korrigiert; gesamt=planen+herstellen; diff=herstellen-planen; korrigiert=gesamt*1.25; (Quelltext 12.3) 11/24 Einleitung Überladen binärer arithmetischer Operatoren Weitere Operatoren Operator + Beschränkungen Operatoren -, * Zeitklasse mit weiteren Operatoren: Problem Multiplikationsoperator erlaubt keine Vertauschung I Multiplikation “gesamt*1.25” möglich (s.o.) I mathematisch äquivalentes “1.25*gesamt” nicht möglich! I wieso nicht? Elementfunktion: erster Operand ist aufrufendes Objekt → aber hier “1.25” kein Objekt! I Lösung: Implementierung des Operators als nicht-Elementfunktion ⇒ Funktion benötigt folgende Signatur (Prototyp): Time operator*(double mult, Time t) ⇒ Funktion benötigt Zugriffsrechte auf Elementvariablen → Deklaration als friend in Klasse Time 12/24 Einleitung Überladen binärer arithmetischer Operatoren Weitere Operatoren Operator + Beschränkungen Operatoren -, * Zeitklasse mit weiteren Operatoren (Version 2) I 12 13 14 15 16 17 18 19 20 21 22 23 24 25 nicht-Elementfunktion wird Teil der Schnittstelle der Klasse class Time{ private: int hours; int minutes; public: Time operator+(const Time t) const; Time operator-(const Time t) const; Time operator*(const float mult) const; friend Time operator*( const float mult, const Time t); Version: mit friend 31 32 33 34 46 47 48 49 int main(){ Time planen(18,30); Time herstellen(60,0); Time gesamt,diff,korrigiert; //Elementfunktion: korrigiert=gesamt*1.25; //nicht-Elementfunktion: korrigiert=1.25*gesamt; (Quelltext 12.4) 13/24 Einleitung Überladen binärer arithmetischer Operatoren Weitere Operatoren Operator + Beschränkungen Operatoren -, * Zeitklasse mit weiteren Operatoren (Version 3) I I 12 13 14 15 16 17 18 19 20 21 22 23 24 25 friend Status kann vermieden werden: nicht-Elementfunktion kehrt lediglich Reihenfolge um aber: Funktion taucht nicht in Deklaration von Time auf class Time{ private: 105 int hours; 106 int minutes; 107 public: 108 Time operator+(const Time t) 109 const; 110 Time operator-(const Time t) 111 const; 112 Time operator*(const float mult) 113 const; 114 //keine ’friend’ Deklaration 115 void Show() const; Time(); Version: ohne friend Time operator*( const float mult, const Time t){ //ruft nur oeffentliche // Elementfunktion mit // umgekehrter Reihenfolge // auf //-> benoetigt keinen // ’friend’ Status return t*mult; }; (Quelltext 12.5) 14/24 Einleitung Überladen binärer arithmetischer Operatoren Weitere Operatoren Operator + Beschränkungen Operatoren -, * Bemerkungen zu nicht-Elementfunktionen I nicht-Elementfunktionen, welche die Funktionalität einer Klasse erweitern ≡ Hilfsfunktionen I Hilfsfunktionen sollten zusammen mit eigentlicher Klasse deklariert und definiert werden ⇒ Quelltext in gleicher Datei wie Klasse anordnen Elementfunktion oder nicht-Elementfunktion? I manche Operatoren erlauben nur Elementfunktion (s.o.) I sonst: beide Optionen meist äquivalent 15/24 Einleitung Überladen binärer arithmetischer Operatoren Weitere Operatoren Operator << Beispiel: komplexe Zahlen Überladen des Operators “<<” zur Ausgabe von Time Bildschirmausgabe durch Objekt cout (Typ ostream) I Operator “<<” ist bereits überladen für diverse Basistypen I wir wünschen uns Folgendes für die Klasse Time: Time film(1,30); cout << film; //gibt Laenge des Films aus ⇒ Implementierung als nicht-Elementfunktion (eigenes Objekt muss dann nicht erster Operand sein) 16/24 Einleitung Überladen binärer arithmetischer Operatoren Weitere Operatoren Operator << Beispiel: komplexe Zahlen Operator “<<” für die Klasse Time (Version 1) I I analog zu Operator “*” in Programm 12.4 Problem: diese Version erlaubt keine Verkettung Version: void 19 20 21 22 23 24 25 26 27 28 29 class Time{ private: int hours; int minutes; public: Time(); Time(int h, int m=0); friend void operator<<( ostream & os, const Time & t); }; 31 32 33 34 54 55 56 57 58 int main(){ Time halbzeit1(0,45); cout << "Halbzeit1: "; cout << halbzeit1; void operator<<(ostream & os, const Time & t) { os << t.hours << " Stunden, " << t.minutes << " Minuten"; } (Quelltext 12.6) 17/24 Einleitung Überladen binärer arithmetischer Operatoren Weitere Operatoren Operator << Beispiel: komplexe Zahlen Operator “<<” für die Klasse Time (Version 2) Verkettung von Ausgabeoperationen: cout<<zeit1<<zeit2 ⇒ jede einzelne Operation muss Objekttyp “ostream” liefern I 14 15 16 17 18 19 20 21 22 23 24 class Time{ private: int hours; int minutes; public: Time(); Time(int h, int m=0); friend ostream & operator<<( ostream & os, const Time & t); }; Version: ostream & 31 32 33 34 35 36 37 38 /* Verkettung funktioniert: */ cout << "Halbzeit1: " << halbzeit1 << "; Halbzeit2: " << halbzeit2 << "; Nachspiel: " << nachspiel << endl; (Quelltext 12.7) 18/24 Einleitung Überladen binärer arithmetischer Operatoren Weitere Operatoren Operator << Beispiel: komplexe Zahlen Beispiel: Klasse für komplexe Zahlen Komplexe Zahlen I komplexe Zahlen besitzen √ Real- und Imaginärteil c = cr + I ci mit: I = −1 und reellen cr , ci I Konsequenzen für arithmetische Operationen → Überladen von Operatoren bietet sich an B es existiert eine Klasse complex in Standardbibliothek → dieser Entwurf dient nur zu Übungszwecken 19/24 Einleitung Überladen binärer arithmetischer Operatoren Weitere Operatoren Operator << Beispiel: komplexe Zahlen Klasse complex Deklaration 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class complex{ private: //Elementdaten double re; //Realteil double im; //Imaginaerteil //-> c=c.re+c.im*I //[’I’ steht fuer wurzel(-1)] public: //Konstruktor: complex(double r=0, double i=0) : re(r), im(i) { } //Zugriffsfunktionen: double real() const {return re;} double imag() const {return im;} //Zusammengesetzte binaere Operatoren: complex operator+=(complex c); complex operator+=(double d); complex operator*=(complex c); complex operator*=(double d); //fehlen: ’-=’,’/=’ }; (Quelltext 12.8) 20/24 Einleitung Überladen binärer arithmetischer Operatoren Weitere Operatoren Operator << Beispiel: komplexe Zahlen Klasse complex (ii) Implementierung 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 complex complex::operator+=(complex c){ re += c.re; im += c.im; return *this; //’this’ pointer zeigt auf das //Objekt selber } complex complex::operator+=(double d){ re += d; return *this; } complex complex::operator*=(complex c){ //Vorschrift zur Multiplikation // zweier komplexer Zahlen: //a*b=(ar+I*ai)*(br+I*bi) // =(ar*br-ai*bi)+I*(ai*br+bi*ar) double r = re*c.real()-im*c.imag(); double i = im*c.imag()+re*c.real(); re = r; im = i; return *this; //’this’ pointer zeigt auf das //Objekt selber } complex complex::operator*=(double d){ re *= d; im *= d; return *this; } 21/24 Einleitung Überladen binärer arithmetischer Operatoren Weitere Operatoren Operator << Beispiel: komplexe Zahlen Klasse complex (iii) Hilfsfunktionen 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 //1. binaere Operatoren complex operator+(complex a, complex b){ complex r = a; return r += b;//Aufruf: complex::operator+=(complex) } complex operator+(complex a, double b){ complex r = a; return r += b;//Aufruf: complex::operator+=(double) } complex operator+(double a, complex b){ complex r = b; return r += a;//Aufruf: complex::operator+=(double) } complex operator*(complex a, complex b){ complex r = a; return r *= b;//Aufruf: complex::operator*=(complex) } complex operator*(complex a, double b){ complex r = a; return r *= b;//Aufruf: complex::operator*=(double) } complex operator*(double a, complex b){ complex r = b; return r *= a;//Aufruf: complex::operator*=(double) } //fehlen: ’-’,’/’ 22/24 Einleitung Überladen binärer arithmetischer Operatoren Weitere Operatoren Operator << Beispiel: komplexe Zahlen Klasse complex (iv) Hilfsfunktionen – 2 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 //2. unaere Operatoren complex operator+(complex a){ return a; } /* complex operator-(complex a){ return a*(double(-1));//Aufruf: complex::operator*=(double) } */ //3. Vergleichsoperatoren bool operator==(complex a, complex b){ return a.real() == b.real() && a.imag() == b.imag() ; } //fehlt: ’!=’ //4. Ein/Ausgabeoperatoren ostream & operator<<(ostream & os, complex c){ os << c.real() << "+I*(" << c.imag() <<")"; return os; } //fehlt: ’>>’ //5. diverse mathematische Funktionen complex conj(complex c){ //konjugiert komplexe Zahl complex r=complex(c.real(),c.imag()*(-1)); return r; } 23/24 Einleitung Überladen binärer arithmetischer Operatoren Weitere Operatoren Operator << Beispiel: komplexe Zahlen Klasse complex (v) Testprogramm 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 int main(){ //einige moegliche Operationen mit Typ ’complex’ complex a = complex(1,2); cout << "a = " << a << endl; complex b = 3; cout << "b = " << b << endl; complex c = a+2.3; cout << "c = a+2.3 = " << c << endl; complex d = 2+b; cout << "d = 2+b = " << d << endl; complex e = a*c; cout << "e = a*c = " << e << endl; return 0; } 24/24 Anhang Zusammenfassung Literatur Mehr Zusammenfassung I Überladen von Operatoren erlaubt intuitiven Syntax bei Operationen zwischen Objekten B sollte wohldosiert eingesetzt werden I Elementfunktion: erster Operand ist aufrufendes Objekt I falls dies nicht erwünscht: nicht-Elementfunktion I überladene Operatoren behalten ihre Eigenschaften: Anzahl der Operanden, Priorität, Assoziativität 1/5 Anhang Zusammenfassung Literatur Mehr Anhang 2/5 Anhang Zusammenfassung Literatur Mehr Weiterführende Literatur I S. Prata, “C++ Primer Plus”, Sams, 2005 I S. Oalline, “Practical C++ Programming”, O’Reilly, 2003 I J. Liberty and B. Jones, “Teach yourself C++ in 21 days”, Sams, 2005 I R. Lischner, “C++ in a Nutshell”, O’Reilly, 2003 I Handbücher erhältlich am Rechenzentrum: (Weblink) I I RRZN, “Die Programmiersprache C”, 17. Auflage, 2008 RRZN, “C++ für C Programmierer”, 13. Auflage, 2005 I Online Dokumentation zum C++ Standard (Weblink) I (freie) Microsoft Software für Studenten am KIT: (Weblink) 3/5 Anhang Zusammenfassung Literatur Mehr Bedingungsoperator “?” Der einzige “ternäre” Operator (mit 3 Operanden) in C++ I Syntax: <bedingung> ? <Anweisung1> : <Anweisung2> I I falls <bedingung> wahr: führt <Anweisung1> aus, sonst: führt <Anweisung2> aus Abkürzung für äquivalentes “if...else” int main (){//Bsp: Operator ? int a=2,b=7,c; c = (a>b) ? a : b; cout << c; return 0;} int main (){//Bsp: if...else int a=2,b=7,c; if (a>b) c=a; else c=b; cout << c; return 0;} 4/5 Anhang Zusammenfassung Literatur Mehr Operatorsymbole, die überladen werden können + & > ^= <<= || () | += &= == ++ [] * ~= -= |= != -new / ! *= << <= , delete % = /= >> >= ->* new [] ^ < %= >>= && -> delete [] 5/5