Überladen
aus Wikipedia, der freien Enzyklopädie
Überladen (engl. overload) bezeichnet in der Informatik bei der Computerprogrammierung die Erstellung von zwei oder mehr Funktionen mit dem selben Namen. Welche Funktion aufgerufen wird, wird anhand der deklarierten Datentypen der Parameter entschieden. Der Typ des Rückgabewertes kann hingegen in den meisten Programmiersprachen nicht zum Überladen herangezogen werden. 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 }
[Bearbeiten] Operatorüberladung
C++ z. B. bietet die Möglichkeit, Operatoren (wie z. B. Rechenoperatoren +, -, *, /) zu überladen. Diese sind standardmäßig schon überladen. Der +-Operator ist nämlich für alle Standard-Zahlentypen wie int, double, usw. vorhanden und ermöglicht die Addition von allen primitiven numerischen Datentypen auf einen int. Der Programmierer hat nun für jede seiner Klassen die Möglichkeit, Operatoren zu überladen. 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) { 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 Operatoren überladen:
new, +, %, ~, >, /=, |=, <<=, >=, --, (), delete, -, ^, !, +=, %=, <<, ==, &&, ,, [], new[], *, &, =, -=, ^=, >>, !=, ||, ->* delete[], /, |, <, *=, &=, >>=, <=, ++, ->
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 einen size_t-Parameter und muss einen void*-Zeiger auf den reservierten Speicherblock zurückgeben. Analog dazu übernimmt der delete-Operator einen Zeiger auf den Speicherblock und muss diesen freigeben. Mit dieser Technik kann man sich sehr leicht einen Mechanismus zum Überwachen des Speichers programmieren, was ohne Operatorüberladung wesentlich umständlicher wäre. Dazu könnte man z. B. mit malloc() 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. Die delete-Funktion ruft free() 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 mit new angelegten Objekte über diese Funktion erstellt werden, während dies bei Überladung in einer Methode nur bei Anlegen von Objekten dieser einen Klasse mit new geschieht. Ist ein Operator sowohl global als auch als Methode definiert, wird die Funktion 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 namens Skalarprodukt() und Kreuzprodukt() vorzuziehen. Sogar die Addition von zwei Strings mit dem Operator +, wie sie von der std::string-Klasse aus der C++-Standardbibliothek geliefert wird, ist ein Grenzfall.