Überladen von Operatoren

Operatorenüberladung ist ein Konzept, welches erst in C++ existiert. Es erlaubt dem Programmierer, die in der Sprache definierten Operatoren mit eigenen Implementation zu versehen, sodass nicht nur die eingebauten Typen, sondern auch selbst hergestellte Typen mittels Operatoren verknüpft werden können. In C musste man für diesen Zweck Funktionen mit eingängigen Namen definieren, welche explizit angesprochen werden mussten. Die Ausprogrammierung solcher überladenen Operatoren ist etwas aufwändig, doch erleichtert sich das Programmieren des eigentlichen Programmes danach erheblich.

Als Beispiel sei hier die Berechnung der Pseudoinverse einer Matrix aufgeführt (ohne weiter auf deren Bedeutung einzugehen). Die mathematische Definition der Pseudoinverse lautet p = (AT · A)-1 · AT. Im folgenden Programmtext zeigt die erste Zeile eine Implementation, wenn keine Operatorenüberladung verwendet wird, die zweite Zeile verwendet Operatorenüberladung.

Matrix p1 = (A.transpose().multiply(A)).invert().multiply(A.transpose()); Matrix p2 = ~(!A * A) * !A;

Details

Die Überladung eines Operators kann genauso wie eine Funktion deklariert oder definiert werden mit der Ausnahme, dass der Funktionsname den Operator mittels des operator-Keywords und dem gewünschten Operator anspricht. Die Bezeichnung operator * benennt somit den Multiplikations-Operator. Der Abstand zwischen dem operator-Keyword und dem Operator kann auch weggelassen werden, hier auf dieser Seite wird er der Lesbarkeit halber jedoch stets geschrieben.

Operatorenüberladung wird normalerweise für selbst definierte Klassen verwendet, womit üblicherweise die Deklaration der Überladung innerhalb der Klassendeklaration geschrieben wird. Im folgenden Beispiel wird in der Klasse Vector der Multiplikations-Operator überladen:

class Vector{ Vector operator *(float a){ // lots of code } };

Durch dieses Beispiel ist es nun innerhalb des restlichen Codes möglich, einen Vektor mit einer float-Zahl zu multiplizieren.

Grundsätzlich ist zu unterscheiden, ob der Operator einen oder zwei Operanden erwartet. Bei Operatoren mit einem Operanden hat die überladende Funktion einen Parameter, bei Operatoren mit zwei Operanden hat sie deren zwei. Bei Überladungen, welche innerhalb einer Klassendeklaration geschrieben werden, ist der erste Parameter stets gegeben durch das this-Objekt, womit die Parameterliste somit um einen Eintrag schrumpft.

Durch die implizite Bereitstellung des this-Objektes innerhalb der Klassendeklaration stellt sich nun das Problem, dass nach obigem Beispiel zwar ein Vektor mit einem float multipliziert werden kann, jedoch nicht umgekehrt: Die Multiplikation eines float mit einem Vektor. Zwar ist nach mathematischem Denken dies äquivalent, doch für die Sprache C++ gibt es eine genau festgelegte Reihenfolge der Parameter.

Glücklicherweise ist es möglich, Operatoren auch ausserhalb von Klassendeklaration zu überladen. In diesem Falle ist der erste Parameter nicht durch ein this-Objekt vorgegeben, womit die volle Parameterliste angegeben werden kann. Folgendes ist eine gültige Definition der Überladung des Multiplikations-Operators ausserhalb der Klassendeklaration:

Vector operator *(float a, Vector& b){ // lots of code }

Diese Überladung steht nicht innerhalb einer Klassendeklaration, sondern kann sich irgendwo im Code befinden. Üblicherweise stellt man die Deklarationen und Definitionen solcher Operatoren jedoch in die Nähe der Klassendeklaration (häufig nach dem Ende der Klassendeklaration). In anderen Quellen werden solche, ausserhalb der Klasse deklarierten Überladungen auch als non-member-functions bezeichnet.

Die Überladung eines Operators ausserhalb einer Klasse existiert ganz allgemein für den Fall von Verknüpfungen eines dem Programmierer nicht zugänglichen Typs, beispielsweise int, float, aber auch Typen, die durch Standardbibliotheken vorgegeben werden. Alle Überladungen, die als ersten Operanden einen nicht zugänglichen Typ erwarten, müssen somit global, ausserhalb der Klasse definiert werden.

Es ist grundsätzlich möglich, sämtliche Operatorenüberladungen ausserhalb von Klassendeklarationen zu schreiben, allerdings sei hier darauf hingewiesen, dass man ausserhalb der Klassendeklaration nur Zugriff auf öffentliche (public) Elemente der Klasse hat.

Eigenschaften von Überladungen

Operatorenüberladungen sind grundsätzlich nichts anderes als Funktionen und habe somit dieselben Eigenschaften. Sie können beispielsweise an einer Stelle deklariert und an einer anderen Stelle definiert werden. Das obige Beispiel sähe beispielsweise so aus:

class Vector{ Vector operator *(float a); }; Vector Vector::operator *(float a){ //lots of code }

Declaration Definition

Eine weitere Eigenschaft von Funktionen ist, dass sie weitere Überladungen erlauben, in dem Sinne, dass mehrere Überladungen eines Operators existieren können, die nicht dieselben Parameter erwarten. Es ist somit beispielsweise möglich, einen weiteren Multiplikations-Operator zu definieren, der den Vektor mit einer Matrix multipliziert.

Funktionen können innerhalb einer Klassendeklaration zudem als const deklariert werden, was eine weitere Überladung eines Operators ermöglicht. In den detailierten Beschreibungen der einzelnen Operatoren finden sich jeweils beide Prototypen (const und nicht-const).

Des weiteren ist anzumerken, dass die überladenden Funktionen im Programmtext explizit als Funktion aufgerufen werden können, indem man sie mit ihrem Namen (Beispielsweise operator *) anspricht. Beispiel:

Vector x2 = x1.operator *(3.);

Restriktionen

Es können nur Operatoren überladen werden, welche bereits in der Sprache definiert sind. Das Erstellen beispielsweise eines selbst erfundenen Wurzeloperators -/ ist nicht möglich.

Die verschiedenen Operatoren definieren teilweise unterschiedliche Regeln, wie sie überladen werden können. Genauere Informationen sind bei den jeweiligen Operatoren beschrieben.

Beim Überladen innerhalb einer Klassendeklaration gelten folgende Einschränkungen für Typen der Parameterliste: Value-Parameter, Referenz-Parameter (&) und Pointer-Parameter (*) werden unterschieden, aber mehrere Überladungen, die sich nur in den Pointer-Parametern unterscheiden, sind nicht erlaubt, da sie von den Compilern nicht unterschieden, oder genauer gesagt, beliebig ineinander umgewandelt werden können.

Beim Überladen ausserhalb einer Klassendeklaration gelten folgende Einschränkung für Typen der Parameterliste: Die Parameterliste muss mindestens einen Value-Parameter oder Referenz-Parameter(&) enthalten. Eine Parameterliste, die nur Pointer-Parameter enthält ist nicht erlaubt, da die Pointer von den Compilern nicht unterschieden, oder genauer gesagt, beliebig ineinander umgewandelt werden können. Eine leere Parameterliste ist nicht erlaubt, da es keinen nullären Operator gibt.

Nicht überladbare Operatoren

Die folgenden Operatoren können NICHT überladen werden:

()Operatorenklammerung
::Bereichsoperator
::Global-Bereichsoperator
? :Bedingungs-Operator
.Feld-Zugriff-Operator
.*Feld-Dereferenz-Operator
sizeof()Typgrösse-Operator
static_cast<>static-Casting-Operator
dynamic_cast<>dynamic-Casting-Operator
const_cast<>const-Casting-Operator
reinterpret_cast<>reinterpret-Casting-Operator
typeid()Informationen über einen Typ
throwAusnahmebehandlungs-Operator

Alle anderen Parameter erlauben Überladungen, jedoch sind einige Operatoren nicht beliebig überladbar. Genaue Informationen findet man bei den entsprechenden Operatoren.