- Überladen (Programmierung)
-
Eine Programmiersprache ermöglicht das Überladen eines Bezeichners, wenn mehrere Vereinbarungen mit demselben Bezeichner gleichzeitig sichtbar sein können; bei der Verwendung erfolgt dann die Auswahl anhand des Kontextes. Mit anderen Worten verdeckt eine Vereinbarung nur dann eine andere, wenn nicht nur der Bezeichner, sondern weitere Merkmale übereinstimmen. Meist handelt es sich um Bezeichner von Unterprogrammen, die aufgrund der Parameter-, bisweilen auch des Resultattypen unterschieden werden. Das Überladen wird, da es sich um einen rein syntaktischen Mechanismus handelt, nach Strachey, als Ad-hoc-Polymorphie betrachtet.
Auch Aufzählungswerte und Literale können überladen werden.
Operatorüberladung
Seit jeher unterschieden die meisten Programmiersprachen, der mathematischen Tradition folgend, nicht zwischen den Operatorssymbolen (+, –, …) für Ganzzahl- (Integer) und Gleitpunktarithmetik (real, float). Zur bruchlosen Erweiterung einer Sprache um benutzerdefinierte Typen ist es hilfreich, die Operatorsymbole auch für benutzerdefinierte Typen überladen zu können.
Ein Beispiel für das Überladen des Operators
+
in C++:class EinfacheKomplexeZahl { public: EinfacheKomplexeZahl() {} EinfacheKomplexeZahl(float real, float imag) { m_real = real; m_imag = imag; } ~EinfacheKomplexeZahl() {} EinfacheKomplexeZahl operator+(const EinfacheKomplexeZahl &o) const { return EinfacheKomplexeZahl(o.m_real + m_real, o.m_imag + m_imag); } private: float m_real, m_imag; }; int main(int argc, char *argv[]) { EinfacheKomplexeZahl z1(5.0f, 3.14159f); EinfacheKomplexeZahl z2(0.0f, -3.14159f); EinfacheKomplexeZahl z3 = z1 + z2; // z3.m_real = 5 und z3.m_imag = 0 return 0; }
In C++ lassen sich fast alle vorhandenen Operatoren überladen.
Die Parameter und Rückgabewerte der Operatorüberladungen müssen für jeden Operator sorgsam gewählt werden, denn manchmal ist er vordefiniert. So übernimmt der
new
-Operator einensize_t
-Parameter und muss einenvoid*
-Zeiger auf den reservierten Speicherblock zurückgeben. Analog dazu übernimmt derdelete
-Operator einen Zeiger auf den Speicherblock und muss diesen freigeben. Mit dieser Technik kann man sich sehr leicht einen Mechanismus zur Überwachung des Speichers programmieren, was ohne Operatorüberladung wesentlich umständlicher wäre. Dazu könnte man z. B. mitmalloc()
mehr Speicher als nötig reservieren und an den Anfang des Speichers eine Struktur mit diversen Informationen über den Speicherblock speichern. Der zurückgegebene Zeiger zeigt auf den Speicher nach der Struktur. Diedelete
-Funktion ruftfree()
dann auf den Beginn der Struktur auf. Außerdem könnte man die Menge an reserviertem Speicher zählen und mit der freigegebenen Menge abgleichen:static std::size_t nichtFreigegebenerSpeicher = 0; struct blockKopf { int magic; std::size_t size; }; void * operator new(std::size_t size) throw (std::bad_alloc) { blockKopf *p = (blockKopf *)malloc(sizeof(blockKopf) + size); if (p == NULL) throw(std::bad_alloc); p->magic = 0815; p->size = size; nichtFreigegebenerSpeicher += size; return (void *)((char *)p + sizeof(blockKopf)); } void operator delete(void *p) { blockKopf *block = (char *)p - sizeof(blockKopf); if (block->magic != 0815) return; nichtFreigegebenerSpeicher -= block->size; free(block); } #include <iostream> int main(int argc, char *argv[]) { // Objekte mit new erstellen und mit delete löschen // ..... std::cout << nichtFreigegebenerSpeicher << " unfreigegebener Speicher!" << std::endl; }
Operatoren können sowohl als globale Funktion als auch als Methode überladen werden. Als Beispiel bedeutet eine globale Überladung für
new
, dass alle mitnew
angelegten Objekte über diese Funktion erstellt werden, während dies bei Überladung in einer Methode nur bei Anlegen von Objekten dieser einen Klasse mitnew
geschieht. Ist ein Operator sowohl global als auch als Methode einer Klasse definiert, wird letztere der globalen Überladung vorgezogen.Ziel der Operatorüberladung ist immer ein einfach lesbarer Quellcode, wobei genau dies bei hoher Abstraktion von Klassen durch die Benutzung von Operatorüberladungen erschwert werden kann. Bei primitiven numerischen Datentypen ist die Überladung von
*
selbsterklärend. Überlädt man diesen Operator allerdings für dreidimensionale Vektoren, ist nicht eindeutig, ob das Skalarprodukt- oder Kreuzprodukt ausgeführt wird. In diesem Fall sind zwei Methoden namensSkalarprodukt()
undKreuzprodukt()
vorzuziehen. Sogar die Konkatenation von zwei Strings mit dem Operator+
, wie sie von derstd::string
-Klasse aus der C++-Standardbibliothek geliefert wird, ist ein Grenzfall.Anzumerken ist, dass Operatorenüberladung normalerweise nur als syntaktischer Zucker verwendet werden soll, das heißt die dem Programmierer zur Verfügung gestellte Funktionalität auch über namentliche Funktionen bzw. Methoden zur Verfügung gestellt werden sollte.
Abgrenzung des Begriffs
Beim dynamischen Binden wird statt des deklarierten Typs der zur Laufzeit tatsächlich angetroffene Typ herangezogen. Dieser Unterschied zwischen dynamischem Binden und Überladen führt oft zu subtilen Programmfehlern.
Beispiel in C++:
// Funktion 1 int quadrat(int i) // Übernimmt einen int { return i * i; } // Funktion 2 int quadrat(const std::string& str) // Übernimmt eine Referenz auf ein String-Objekt mit konstanten Member-Variablen aus der STL { int t = atoi(str.c_str()); return t * t; } int main() { std::string s("2"); int i = 3; int k = quadrat(s); // ruft die überladene Funktion für String-Parameter auf => k == 4 int m = quadrat(i); // ruft die Funktion für Parameter vom Typ int auf => m == 9 }
Siehe auch
Wikimedia Foundation.