Was ist das hier?

  • Eine Plattform von Flensburger Studenten für Flensburger Studenten
  • Ein Wiki zum Sammeln von Wissen
  • Ein Forum zum Austausch
  • Eine Wissensdatenbank zum Informatikstudium:
    • Programmieren in C, C#, PHP, Javascript, HTML, CSS
    • Datenbanken und Abfragen mit SQL
    • 2D / 3D - Gestaltung
    • Mathe, Physik, Gerätetechnik, RoBs
    • Audio- / Videotechnik
    • ...und vieles mehr

Anmelden

Warum registrieren?

Weil besser als gut!

 

Operatorüberladung (C++)

Operatorüberladung (operator overloading)

Operatoren können überladen werden, allerdings gelten folgende Einschränkungen:

  • es können keine neuen Operatoren eingeführt werden, nur die bestehenden Operatoren können überladen werden
  • die Anzahl der Operanden kann nicht verändert werden ( z.B.: [unäres ++ bleibt immer unär) zur Erklärung von unär siehe Einstellige Verknüpfung (Wikipedia)
  • die Rangfolge der Operatoren kann nicht verändert werden
  • der Typ mindestens eines Operanden muss benutzerdefiniert sein. Dies verhindert, dass die Standardoperatoren überschrieben werden.
  • folgende Operatoren können nicht überladen werden:
 .
.*
::
?:

Operatoren können generell realisiert werden als:

  • globale Funktionen (non-member functions)
  • members oder
  • friends

Die Operatoren = () [] -> können nur als member function realisiert werden.

Die Syntax der Operatorfunktionen ist identisch zur Syntax 'normaler' Funktionen, allein der Funktionsbezeichner lautet:

operatorop

Beispiel

Möchte man etwa den Operator * überladen, um Objekte der Klasse Mitarbeiter zu multiplizieren:

C++
Mitarbeiter operator*( Mitarbeiter links, Mitarbeiter rechts )
{
	// programmcode...
}

Der Aufruf dieser Funktion erfolgt alternativ in Funktions- oder Operatorschreibweise (Tom und Jerry seien Objekte der Klasse Mitarbeiter):

C++
Tom = operator*(Tom, Jerry);
Tom = Tom * Jerry;

Hinweis: Natürlich ist es Unsinn, Mitarbeiter zu multiplizieren.

Es wird dringend empfohlen, sich nicht zu weit von der ursprünglichen Semantik eines Operators zu entfernen!

weitere Aufrufvarianten

Beim Aufruf ist außerdem relevant, ob es sich um:

  • member / non-member
  • binäre oder
  • unäre (pre- oder postfix) Funktionen handelt

Als member function hätte die Multiplikation nur einen Parameter (der andere ist das hidden argument):

C++
Mitarbeiter Mitarbeiter::operator*(Mitarbeiter rechts)
{
	// Programmcode...
}

Aufrufe in Funktions- oder Operatorschreibweise :

C++
Tom = Tom.operator*(Jerry);
Tom = Tom * Jerry; // unverändert!

Aufrufe unärer Operatoren

Alle unären Operatoren sind prefix Operatoren, nur ++ und -- gibt es als pre- und postfix Variante. Somit gibt es 4 Möglichkeiten:

member function, prefix

C++
void Mitarbeiter::operator++( ) { ... }

Aufrufe:

C++
Tom.operator++();
++Tom;

member function, postfix

C++
void Mitarbeiter::operator++( int dummy ) // dummy Argument dient nur zur Unterscheidung von prefix / postfix
{
 	// Programmcode ... 
}

Aufrufe:

C++
Tom.operator++(42);
Tom++;

non-member function, prefix

C++
void operator++( Mitarbeiter m ) { ... }

Aufrufe:

C++
operator++(Tom);
++Tom;

non-member function, postfix

C++
void operator++(Mitarbeiter m, int dummy) // dummy Argument kennzeichnet postfix
{
	// Programmcode ...
}

Aufrufe:

C++
operator++(Tom, 42);
Tom++;

Bemerkungen zum Beispiel op01.cpp

op01.cpp
Der Programmcode zum Beispiel op01.cpp
  • die Operatorfunktion ist global definiert
  • die Attribute der Klasse sind public
  • der erste Konstruktor überschreibt den Default-Konstruktor, der in der Operatorfunktion benötigt wird (Objekt tmp instanziieren)
  • main: die neue Addition funktioniert auch mit gemischten Typen, solange der Compiler eine Konvertierung zu MeinTyp findet. Hier werden die Integerliterale 1 und 2 mit dem Konstruktor MeinTyp(int) umgewandelt.

Typwandler

eigene 'Typwandler' können sein:

conversion functions
Konstruktoren mit einem Argument, wobei der Typ des Arguments nicht der eigene Typ ist (wandelt z.B. von int -> MeinTyp)
casting operators
Funktionsbezeichner ist operator typ , kann auch überladen werden, Aufruf z.B.: int(ObjektMeinesTyps) (wandelt von MeinTyp -> int)

Erweiterung

Das obige Beispiel hat keinen Bezug zur OOP - alle Attribute sind public, eine globale Funktion greift direkt darauf zu.

Dies soll nun behoben werden: die Daten werden private und die Operatorfunktion wird member der Klasse, so dass sie Zugriff auf die Daten hat.

Test

  • das Programm wird nicht übersetzt, Fehler in Anweisung x = 2 + x;
  • wenn Sie die Anweisung auskommentieren, sollte das Programm funktionieren, insbesondere wird die Anweisung x = x + 1; korrekt ausgeführt!

Schreibt man die beiden Operatoraufrufe in Funktionsschreibweise, wird das Problem klar:

C++
x.operator+(1); // o.k., 1 wird mit dem Konstruktor konvertiert
2.operator+(x); // 2: nicht möglich, keine Konvertierung

Nicht statische Methoden verhalten sich 'asymmetrisch', da für das hidden argument keine benutzerdefinierten Konvertierungen vorgenommen werden.

Anders ausgedrückt: das hidden argument ('this pointer argument') muss immer den Typ Zeiger auf Objekt der eigenen Klasse haben.

Will man symmetrisches Verhalten wie in op01 und dennoch auf private Daten nicht verzichten, bleibt nur, einen friend-Operator anzulegen.

Anmerkung: Eine weitere (ähnliche) Situation, die einen friend-Operator erfordert, werden wir später beim Überladen von << kennenlernen.

Unäre Operatoren

Dieses Problem tritt bei unären Operatoren nicht auf, sie sollten möglichst immer als member implementiert werden.

Ergebnis: Der friend Operator löst das Symmetrieproblem, die Daten können privat deklariert werden

Beispiel
Der Programmcode zum Beispiel op03.cpp

Überladung der Zuweisung

Die Default-Zuweisung erzeugt keine tiefe Kopie (s. auch copy-Konstruktor, Prog2 - Labor Nr. 4). Durch Überladung des = Operators kann der Mangel jetzt behoben werden:

Da als L-value (linker Operand von =) immer nur ein Objekt der eigenen Klasse erlaubt sein soll, tritt das Symmetrieproblem nicht auf. Der Operator kann damit als member implementiert werden:

  • die Zuweisung hat 2 Operanden: linker Operand entspricht hidden argument, rechter Operand entspricht dem einen, weiteren Parameter
  • der Parameter sollte eine konstante Referenz sein
  • der return type ist void
  • Rumpf wie beim Copy Konstruktor, jedoch mit Deallokation
| Mehr

Zahlen & Daten

  • 1253 Seitenaufrufe
  • 1115 Tage alt
  • 22 Versionen
  • Letzte Änderung: 30.04.2009 um 09:28 Uhr

Publish