Einführung in C++ Operatoren überladen (Klassen) Operatoren überladen Die Überladung von Operatoren ermöglicht es, vorhandene Operatoren (+,-,...) auch auf Objekte von Klassen anzuwenden. Die meisten der C++ Operatoren für elementaren Datentypen lassen sich nicht sofort auf Klassenobjekte anwenden. Ausnahme: z.b. ein Operator, der automatisch bei Klassen vorhanden ist, ist der Zuweisungsoperator (=). Arithmetische Operatoren können nur verwendet werden, wenn sie auch für Klassenobjekte überladen werden. Die Überladung eines Operators findet immer in Zusammenhang mit mindestens einer Klasse statt, d.h. mindestens einer der beiden Typen muss ein benutzerdefinierter Typ (Klasse) sein. 1
Regeln: Operatoren überladen Dabei wird der Definitionsbereich des Operators erweitert Es können keine neuen Operatoren erfunden werden: ***, @ oder ;-) funktioniert nicht Die Bedeutung der Operatoren für Standardtypen lässt sich nicht umdefinieren (z.b. Addition + für int) Die Anzahl der Operanden eines Operators kann nicht geändert werden: Binärer Operator bleibt stets binär (*, -, ) Unärer Operator bleibt stets unär (++, --, Vorzeichen). Operatoren müssen alle explizit überladen werden Überladen von + und += Überladbare Operatoren Operatoren, die man nicht überladen kann..* ::?: sizeof Operatoren + - * / % ++ -- = =!= < <= > >= &&! = op= & ^ ~ << >> () [] & * ->, new delete Zugriffsoperatoren Auswahloperator, Speicherplatz Bedeutung arithmetische Operatoren Vergleichsoperatoren Logische Operatoren Zuweisungsoperator (op ist binärer arithmetischer Operator) Bitoperationen Funktionsaufruf, Indexoperator Sonstige Operatoren 2
Operatoren überladen für Klassen Operatoren für Klassen: Man kann die Funktionalität einer Klasse nicht nur durch Methoden, sondern auch durch Operatoren festlegen. Beispiel: object3 = object1.add(object2); object3 = object1 + object2; schwer zu lesen klar Vorteil: Ausdrücke mit Operatoren sind oft intuitiver und schneller zu erfassen als Methodenaufrufe. Operatoren überladen Definition binärer Operatoren: definiert den binären Operator für Instanzen der Klasse X, wobei ein Operator ist, der für elementare Datentypen existiert. Verwendung class X // declaration X operator (const X & right); // definition X X::operator (const X & right)... X x, y, z; x = y.operator(z) x = y z; 3
Operatoren überladen Definition unärer Operatoren: definiert den unären Operator für Instanzen der Klasse X, wobei ein Operator ist, der für elementare Datentypen existiert. Verwendung class X // declaration X operator (); // definition X X::operator ()... X x, z; x = z.operator(); x = z; Operatoren überladen Beispiele: x = y; // equivalent of x.operator=(y); i += 1; // i.operator+=(1); if( q == r ) // q.operator==(r) or operator==(q, r) cout << x; // cout.operator<<(x); or operator<<(cout, x); a = b + c; // a = b.operator+(c); or a = operator+(b, c); Es gibt zwei Möglichkeiten, Operatoren zu überladen: Als Operatormethode innerhalb der Klasse X z.b. // addition, binary operator X operator+( const X& y) const; Als globale Operatorfunktion ausserhalb aller Klassen z.b. // addition, binary operator X operator+(const X& y, const X& z); 4
Operatoren überladen Beispiel für Überladen von Operatoren Klasse IntArray (Lekt. 5/Folien 10) class IntArray private: int *data; int len; public: // Konstruktoren,Methoden,Operatoren ; Besser Operatoren als Methoden (nächsten Folien) Klasse IntArray Operator überladen = Neue Definition des Verhaltens für diese Klasse class IntArray public: // assignment operator: a = b IntArray& operator=(const IntArray& b); // addition, binary operator: a += b IntArray& operator+=(const IntArray& b); // addition with an int, binary op: a += i IntArray& operator+=(int i); // addition, binary operator: c = a+b IntArray operator+( const IntArray& b) const; // equality test, binary op. a == b bool operator==(const IntArray& b) const; // prefix increment, unary op. ++a IntArray& operator++(); // postfix increment, unary op. a++ IntArray& operator++(int );... private: int len; int* data; ; 5
Klasse IntArray: Zuweisungsoperator IntArray& IntArray::operator=( const IntArray& rhs) if (this == &right) // check for self-assignment return *this; // for arrays of different sizes, de-allocate original // left-side array, then allocate new left-side array if (len!= right.len) delete [] data; // reclaim space len = right.len; // resize this object data = new int [len]; // create space for array copy for (int i=0; i<len; ++i) // copy array into object data[i] = right.data[i]; return *this // enables x=y=z, for example Klasse IntArray: Additionsoperatoren // addition, binary operator: a += b IntArray& IntArray::operator+=( const IntArray& v) assert(len == v.len); for (int i=0; i<len; ++i) data[i] += v.data[i]; Ändere das Objekt return *this; (this) Objekt zurückgeben // addition, binary operator: c = a+b IntArray IntArray::operator+( const IntArray& v) const IntArray retval(*this); Erzeuge neues Objekt retval += v; return retval; Neues Objekt zurückgeben 6
Klasse IntArray: Vergleichsoperator bool IntArray::operator==(const IntArray& v) const if (len!= v.len) return false; for (int i=0; i<len; ++i) if (data[i]!= v.data[i]) return false; return true; Klasse IntArray: Präfix-/Postfixoperatoren Der unäre Inkrement Operator (++) für Klassen ist speziell, weil es eine Prefix und eine Postfix Notation gibt: Prefix-Fall (++a): gibt den inkrementierten Wert zurück. Postfix-Fall (a++): gibt den alten Wert als Kopie zurück und inkrementiert a. Wir definieren zuerst die Prefix Version der Methode: // prefix increment IntArray& IntArray::operator++() for (int i=0; i<len; ++i) ++data[i]; return *this; Aufruf: IntArray a; ++a; // a.operator++(); 7
Klasse IntArray: Präfix-/Postfixoperatoren Postfix-Version der Methode: ein bisschen komplizierter, weil der alte Wert zurückgegeben wird. Konvention: Der Postfix-Operator wird durch einen Dummy- Parameter (int) vom Prefix-Operator unterschieden: // postfix increment IntArray IntArray::operator++(int) // create copy of this IntArray tmp(*this); // increment *this object for (int i=0; i<len; ++i) data[i]++; // return copy return tmp; Aufruf: IntArray a; a++; // a.operator++(int i) Klasse IntArray: Index-Operator überladen Vektoren/Arrays Der Index Operator für Vektoren/Arrays v impliziert eine versteckte Zeigerarithmetik: v[i] ist gleichbedeutend mit *(v+i) Es gelten für den Index-Operator bei Vektoren Einschränkungen: Ein Operand muss ein Zeiger sein. Der andere Operand muss ein ganzzahliger Ausdruck sein. Der Ergebnistyp ist festgelegt. Verwendung bei Klassen Diese Einschränkungen gelten bei Klasse nicht: Der linke Operand muss ein Objekt der Klasse sein. Der rechte Operand darf ein beliebiger Datentyp sein. Der Ergebnistyp ist nicht festgelegt. 8
Klasse IntArray: Index-Operator überladen class IntArray int *data; int len; public: IntArray(int l) len = l; data = new int[len]; for (int i=0; i< len; i++) data[i] = i; int& operator[](int i) if (i<0 i>=len) cerr << "Out of Range!" << endl; exit(1); //Referenz auf i-tes Element return *(data+i); ; int main(int argc, char* argv[]) // ctor for 5 elements IntArray a(5); for (int i=0; i< 6; i++) cout << a[i] << endl; austin:~/test/a>./int_test 0 1 2 3 4 Out of Range! Geht aber zu Lasten der Effizienz!! Überladen von Operatoren Beispiel für Überladen von Operatoren Klasse Complex (Lekt. 5/Folie 24) class Complex private: double real, imag; public: display( ); // as (a + bi) double doublevalue( ); // res = this Complex add( Complex other ); // res = this + other Complex sub( Complex other ); // res = this - other Complex mult( Complex other );// res = this * other Complex div( Complex other ); // res = this / other Complex conjunct( ); // c' // Konstruktoren,Methoden,Operatoren ; Besser Operatoren als Methoden (nächsten Folien) 9
Klasse ComplexÜberladen von Operatoren (1) Vergleichsoperator Methode ändert die Objekte nicht class Complex... public: Definiere == mit einem Argument // comparison bool operator ==( const Complex& other ) const return (real == other.real && imag == other.imag); ; // c == other Klasse ComplexÜberladen von Operatoren (2) Arithmetische Operatoren: 1.) unär: -c, ++c, --c 2.) binär: c+b, c-b, c*b, c/b, c+=b. c-=b,... Warum ++c und und nicht c++? Complex operator++( int i=0) Complex tmp= Complex(*this); real+=1; return tmp; ; // c++ class Complex public: // Vorzeichen b = -c Warum const? Complex operator-( ) const Complex result = Complex( -real, -imag ); return result; Complex& operator ++( ); real+=1.0; // ++c return *this;;// ++c Complex& operator --( ); real-=1.0; // --c return *this;;// --c Wie sieht die Inline Methode aus? Warum nicht const? 10
Globale Operatoren Operatorfunktion: Global oder als Methode? class Complex private: double real, imag; public: // Konstruktor ; 3 Konstruktoren: Complex( ), Complex(1.0) Complex(4,2) Complex (double r=0.0, double i=0.0) : real(r), imag(i) ; // res = c + other Complex operator+(const Complex& other ) const Complex result= Complex(real+other.real,imag+other.imag); return result;... int main( ) Complex c1, c2, c3; c1 = c2 + c3; // c2.operator+(c3) c1 = c2 + 4.3; // automatic: Complex + double->complex c1 = 4.3 + c2; // first operand must be Complex Globale Operatoren Operatorfunktion: Global oder als Methode? Globale Operatoren werden vorzugsweise eingesetzt, wenn Der Operand binär ist und in beiden Operanden symmetrisch, z.b. die arithmetischen Operatoren + oder * Der Operand soll für eine fremde Klasse überladen werden, ohne die Klasse zu ändern, z.b. << für die Klasse ostream Definition globaler Operatorfunktion: Bei globalen Operatorfunktionen werden Operanden als Argument übergeben: Binärer Operator: 2 Argumente Unärer Operator: 1 Argument Complex& operator+(const Complex& c1, const Complex& c2); 11
Globale Operatoren Ein erstes Beispiel einer globalen Operatorfunktion class Complex // Ohne die Operatorfunktion für + und // sonst unverändert also auch mit += und -= ; // globale Operatorfunktion inline Complex operator+(const Complex& c1, const Complex& c2) Complex tmp(c1); tmp += c2; return tmp;... c1 = 4.3 + c2; // this is now possible Der globale Operator hat keinen Zugriff auf die privaten Klassenelemente. Die Funktion verwendet daher den Operator +=, dessen Operatorfunktion als Methode definiert ist. Globale Operatoren Um einer globalen Operatorfunktion den Zugriff auf die privaten Elemente zu erlauben, kann sie als Freund (friend) der Klasse definiert werden. class Complex... // deklaration global operators (friend) friend Complex operator+(const Complex&, const Complex& );... // c + other inline Complex operator+(const Complex& c1, const Complex& c2) Complex result = Complex(c1.real+c2.real, c1.imag+c2.imag); return result; ;... Privates Klassenelement in Complex 12
Globale Operatoren: Shift-Operatoren für die Ausgabe Soll ein Objekt c der Klasse Complex auf dem Bildschirm ausgegeben werden cout << a, so erhält man eine Fehlermeldung des Compilers. int i; cout << i; // operator<<(cout, i) Globale Operatoren: Shift-Operatoren für die Ausgabe Den Operator << überladen class Complex... // deklaration global operators (friend) friend ostream& operator<< (ostream&, const Complex&); ostream& operator<<( ostream& o, const Complex& c ) if (c.imag==0) o<< c.real; // as real number else if (c.real==0 ) o<< c.imag << i ;// pure imaginary else o << "(" << c.real; if ( c.imag >= 0 ) o << '+'; if ( c.imag < 0 ) o << -'; o << c.imag << "i)"; return o; // output << // (a+bi) or (a-bi) Die Rückgabe von ostream bewirkt, dass man mehrere Komponenten hintereinander mit << verküpfen kann cout << a << << b; 13