Überladen

aus Wikipedia, der freien Enzyklopädie
Dies ist die aktuelle Version dieser Seite, zuletzt bearbeitet am 20. Oktober 2021 um 06:15 Uhr durch imported>L47(533607) (→‎Operatorüberladung: Fehlende Satzzeichen ergänzt.).
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)

Überladen (von englisch overloading) bedeutet in der Softwareentwicklung das bewusste Etablieren von Polymorphien, sodass ein und dasselbe Sprachelement – Operatoren, Konstruktoren, Literale und dergleichen – unterschiedliche, aus dem Kontext hervorgehende Bedeutungen annehmen können. Das Überladen wird, da es sich um einen rein syntaktischen Mechanismus handelt, nach Strachey als Ad-hoc-Polymorphie betrachtet.

Nicht alle Programmiersprachen ermöglichen das Überladen, als Verfahren ist es insbesondere bei objektorientierten Programmiersprachen anzutreffen.

Erläuterung an einem Beispiel

In vielen Programmiersprachen steht der Operator + für verschiedene, durchaus unterschiedliche, Operationen:

1 + 2
"abc" + "def"

Im ersten Fall ist das erwartbare Ergebnis "3", also eine Integer-Addition, im zweiten "abcdef", also eine String-Konkatenation. Dies sind im Grunde zwei sehr unterschiedliche Operationen. Compiler oder Interpreter von Sprachen, die eine solche sogenannte Polymorphie erlauben, tun dies, indem sie die anzuwendende Methode (Addition oder Konkatenation) aufgrund des Datentyps der beteiligten Operanden auswählen.

Von Überladung spricht man nun, wenn eine solche Polymorphie nicht nur, wie im Falle des Beispiels, vorgefertigt in einer Sprache vorhanden ist, sondern darüber hinaus auch durch Sprachkonstrukte weitere solcher Polymorphien geschaffen werden können. Im obigen Beispiel etwa könnte die Sprache die Möglichkeit bieten, durch Programmtext eine zusätzliche Bedeutung des +-Operators für andere Datentypen (etwa Arrays) zu etablieren. (Dies wäre dann ein Beispiel für Operatorüberladung.)

Methodenüberladung

Methodenüberladung liegt vor, wenn mehrere Methoden denselben Namen haben, sie aber verschiedene Parameter (engl. argument) erwarten. Welche Methode genau aufgerufen wird, wird dann bei jedem Aufruf anhand der Parameter und ihrer Datentypen automatisch vom Compiler bestimmt. Ein typisches Beispiel ist eine Methode, die sowohl Texte (Strings) als auch ganze Zahlen (Integer) auf dem Bildschirm ausgeben können soll. Beispiel aus Pascal:

procedure GibAus(text: String); overload;
begin
    writeln(text); // übergebenen Parameter ausgeben
end;

procedure GibAus(zahl: Integer); overload;
var
    zahlAlsText: String;
begin
    zahlAlsText := IntToStr(zahl); // übergebene Zahl in einen Text umwandeln
    writeln(zahlAlsText); // ausgeben
end;

begin
    GibAus('Hallo Welt!'); // gibt den Text "Hallo Welt!" aus
    GibAus(4711);          // gibt die Zahl 4711 aus
end;

Im Falle von Funktionen ist in manchen Sprachen auch die Überladung anhand des Ergebnis-Typs möglich.

Nachteil ist ein Verlust an Übersichtlichkeit und dass Probleme auftreten können, wenn nachträglich weitere Überladungen hinzugefügt werden (vgl. das unten verlinkte Wikibook).

Abgrenzung

Standardwerte

Keine Methodenüberladung im eigentlichen Sinne ist der Standardwert (engl. default argument), welcher ebenfalls dazu führt, dass eine – allerdings dieselbe – Methode mit unterschiedlichen, nämlich unterschiedlich vielen, Parametern aufgerufen werden kann. Wird ein Parameter ausgelassen, wird hierbei stattdessen der Standardwert übergeben:

procedure GibAus(text: string = 'kein Parameter übergeben');
begin
    writeln(text); // übergebenen Parameter ausgeben
end;

begin
    GibAus('Hallo Welt!'); // gibt "Hallo Welt!" aus
    GibAus();              // gibt den Standardwert "kein Parameter übergeben" aus
end;

Methoden mit Standardwert können problemlos in überladene Methoden überführt werden. Obiger Quelltext ist funktional mit folgendem identisch:

procedure GibAus(text: String); overload;
begin
    writeln(text); // übergebenen Parameter ausgeben
end;

procedure GibAus(); overload;
begin
    GibAus('kein Parameter übergeben'); // fehlenden Parameter ergänzen
end;

begin
    GibAus('Hallo Welt!'); // gibt "Hallo Welt!" aus
    GibAus();              // gibt den Standardwert "kein Parameter übergeben" aus
end;

Dies ist hilfreich in Sprachen, die Standardwerte nicht unterstützen, beispielsweise Java.

Implizite Typumwandlungen

Ebenfalls von der Methodenüberladung zu unterscheiden sind implizite Typumwandlungen. In vielen Programmiersprachen lässt sich eine einzelne Methoden, die einen Gleitkommawert (Real) als Wertparameter erwartet, auch mit einer ganzen Zahl (Integer) aufrufen. Eine implizite Typumwandlung ist bei einem Referenzparameter nicht möglich, da formale und tatsächliche Parameter übereinstimmen müssen:

function QuadriereWertparameter(basis: Real): Real;
begin
    result := basis * basis; // Ergebnis als Funktionswert zurückgeben
end;

procedure QuadriereReferenzparameter(var basis: Real);
begin
    basis := basis * basis; // Ergebnis dorthin schreiben, wo der Parameter herkam
end;

var
    zahl: Integer;
begin
    zahl := 4711;
    QuadriereWertparameter(zahl); // implizite Typumwandlung (Ergebnis der Funktion wird verworfen)
    QuadriereReferenzparameter(zahl); // Compilerfehler, denn die aufgerufene Methode könnte keine Kommazahl zurückgeben, obwohl eine Kommazahl als Referenzparameter deklariert wurde
end;

Würde man im obigen Quelltext die folgende überladene Methode (und die overload-Direktive bei der vorhandenen Methode) ergänzen, wäre das Programm kompilierbar:

procedure QuadriereReferenzparameter(var basis: Integer); overload;
begin
    basis := basis * basis;
end;

Operatorüberladung

Seit jeher unterschieden die meisten Programmiersprachen, der mathematischen Tradition folgend, nicht zwischen den Operatorsymbolen (+, -, …) 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++:

struct komplexe_zahl {
    float real = 0;
    float imag = 0;
};

komplexe_zahl operator+(komplexe_zahl links, komplexe_zahl rechts) {
    return komplexe_zahl {links.real + rechts.real, links.imag + rechts.imag};
}

int main() {
    komplexe_zahl z1 {5.0f, 3.14159f};
    komplexe_zahl z2 {0.0f, -3.14159f};
    komplexe_zahl z3 = z1 + z2; // z3.m_real = 5 und z3.m_imag = 0
}

In C++ lassen sich fast alle vorhandenen Operatoren überladen, was in den meisten Fällen sowohl über freistehende Funktionen (wie hier) als auch über Methoden erfolgen kann.

Ziel der Operatorüberladung ist die leichte Lesbarkeit des Quelltext, wobei stets beachtet werden sollte, inwieweit diese durch überladene Operatoren verbessert oder verschlechtert wird: Während beispielsweise (a + b) * c klar besser zu lesen ist als mal(plus(a, b), c) ist dies bei weitem nicht immer der Fall, insbesondere wenn die konkrete Überladung nicht weithin bekannten Konventionen folgt.

Zuletzt ist anzumerken, dass es sich bei Operatorenüberladung nur um syntaktischen Zucker handelt und es in aller Regel genau so möglich wäre die gewünschte Funktionalität auch durch normale Funktionen bzw. Methoden zu implementieren.

Siehe auch

Weblinks