C++: Operatoren
In C++ können existierende Operatoren überladen werden, etwa für die Nutzung mit eigenen Klassen. Dabei kann die Überladung innerhalb einer Klassendefinition passieren (analog zur Implementierung einer Methode) oder außerhalb der Klasse (analog zur Definition einer überladenen Funktion).
Beim Überladen in einer Klasse hat der Operator nur einen Parameter (beim Aufruf das Objekt auf der rechten Seite) und man kann auf die Attribute der Klasse direkt zugreifen. Bei der Überladung außerhalb der Klasse hat der Operator zwei Parameter und darf nicht auf die Attribute der Klasse zugreifen.
Man kann Funktionen, Methoden/Operatoren und Klassen als friend
einer Klasse deklarieren. Damit bricht
man die Kapselung auf und erlaubt den Freunden den direkten Zugriff auf die internen Attribute einer Klasse.
Um bei der Implementierung von Post- und Präfix-Operatoren die Variante für den Compiler unterscheidbar zu
machen, hat die Signatur der Postfix-Variante einen Dummy-Parameter vom Typ int
. Dieser wird beim Aufruf
aber nicht genutzt.
- (K2) Implizite Typkonvertierungen bei Operatoren
- (K3) Überladen von Operatoren (innerhalb bzw. außerhalb einer Klasse)
- (K3) Anwendung der Deklaration als
friend
- (K3) Implementierung von Post- und Präfix-Operatoren
Überladen von Operatoren in Klassen
MyString a, b("hallo");
a = b; // ???
a.operator=(b);
Aufruf a=b
ist äquivalent zu a.operator=(b)
Überladen ähnlich wie bei Methoden:
class MyString {
MyString &operator=(const MyString &s) {
if (this != &s) {
// mach was :-)
}
return *this;
}
};
Analog weitere Operatoren, etwa operator==
, operator+
, ... überladen
Überladen von Operatoren außerhalb von Klassen
MyString a("hallo");
cout << a << endl;
class MyString {
ostream &operator<<(ostream &o) { return o << str; }
};
So funktioniert das leider nicht!
- Erinnerung:
cout << a
entsprichtcout.operator<<(a)
- Operator kann nicht in
MyString
überladen werden! - Klasse
ostream
müsste erweitert werden => Geht aber nicht, da System-weite Klasse!
- Operator kann nicht in
=> Lösung: Operator außerhalb der Klasse überladen => 2 Parameter
Überladen von Operatoren außerhalb von Klassen (cnt.)
Operator außerhalb der Klasse überladen => 2 Parameter
ostream &operator<<(ostream &out, const MyString &s) {
return out << s.str;
}
- Nachteil: Benötigt Zugriff auf Klassen-Interna
-
entweder umständlich über Getter-Funktionen
-
oder als
friend
der KlasseMyString
deklarierenAlternativ Zugriffsmethoden (aka Getter) nutzen wie
toString()
...
-
Anmerkung: Rückgabe der Referenz auf den Stream erlaubt die typische
Verkettung: cout << s1 << s2 << endl;
Meine Freunde dürfen in mein Wohnzimmer
void test();
class TestDummy {
int ganzTolleMethode();
};
class Dummy {
private:
int *value;
friend class TestDummy;
friend int TestDummy::ganzTolleMethode();
friend void test();
};
(Fast) alle Operatoren lassen sich überladen
-
Alle normalen arithmetischen Operatoren
-
Zuweisung, Vergleich, Ein-/Ausgabe
-
Index-Operator
[]
, Pointer-Dereferenzierung*
und->
, sowie()
,new
unddelete
(auch in[]
-Form) -
Ausnahmen:
- .
- ::
- ?:
- sizeof
-
Anmerkungen:
- Beim Überladen muss die Arität erhalten bleiben
- Nur existierende Operatoren lassen sich überladen => Es lassen sich keine neuen Operatoren erschaffen
Vgl. Tabelle 9.1 (S. 318) im [Breymann2011]
Implizite Typkonvertierungen bei Aufruf
MyString s;
s != "123"; // ???
"123" != s; // ???
-
Operatoren in Klasse überladen: Typ der linken Seite muss exakt passen
class MyString { public: MyString(const char *s = ""); bool operator!=(const MyString&); }; MyString s; s != "123"; // impliziter Aufruf des Konstruktors, danach MyString::operator!= "123" != s; // KEIN operator!=(char*, MyString&) vorhanden!
Das ist letztlich wie bei einem Methodenaufruf: Um die richtige Methode aufzurufen, muss der Typ (die Klasse) des Objekts bekannt sein.
-
Operatoren außerhalb überladen: Konvertierung auf beiden Seiten möglich
class MyString { public: MyString(const char *s = ""); }; bool operator!=(const MyString&, const MyString&);
NIEMALS beide Formen gleichzeitig für einen Operator implementieren!
Anmerkung zu "++" und "-$\,$-" Operatoren: Präfix und Postfix
-
Präfix:
o1 = ++o2;
- Objekt soll vor Auswertung inkrementiert werden
- Signatur:
Typ &operator++()
-
Postfix:
o1 = o2++;
- Objekt soll erst nach Auswertung inkrementiert werden
- Signatur:
Typ operator++(int)
(=>int
dient nur zur Unterscheidung der Präfix-Variante, wird nie benutzt)
Weitere Anmerkungen
-
Operatoren werden nicht vom System zusammengesetzt
operator+
undoperator+=
sind zwei verschiedene Operatoren!- Implementierung ist prinzipiell unabhängig!
=> Erwartung:
operator+=
$\;==\;$ (operator+
$\;+\;$operator=
)
-
Operatoren lassen sich in C++ verketten:
Dummy a(0); Dummy b(1); Dummy c(2); a = b = c; // a.operator=(b.operator=(c));
-
Übertreiben Sie nicht!
Firma f; Person p; f += p; // ??!
Nutzen Sie im Zweifel lieber Methoden mit aussagekräftigen Namen!
Wrap-Up
- Überladen von Operatoren (innerhalb und außerhalb einer Klasse)
- Innerhalb: 1 Parameter (Objekt auf der rechten Seite)
- Außerhalb: 2 Parameter
- Zugriff auf Attribute:
friend
einer Klasse - Implementierung von Post- und Präfix-Operatoren
Operator "++"
Betrachten Sie die folgende Klasse:
class Studi {
public:
Studi(int credits);
~Studi();
private:
int *credits;
};
Implementieren Sie den operator++
sowohl in der Präfix- als auch in der Postfix-Variante.
C'toren und Operatoren: Was muss noch deklariert werden?
class Studi {
public:
Studi(int credits);
private:
int *credits;
};
int main() {
Studi a(1), b, *c = new Studi(99);
b = *c+a+1;
std::cout << "b: '" << b << "' credits" << std::endl;
return 0;
}
Schreiben Sie Code, damit folgender Code kompiliert:
test wuppie;
bool fluppie = wuppie(3);
- [Breymann2011] Der C++ Programmierer
Breymann, U., Hanser, 2011. ISBN 978-3-446-42691-7. - [cppreference.com] C and C++ Reference
, cppreference.com. - [cprogramming.com] C Programming and C++ Programming
Allain, A. und Hoffer, A..