Programmieren in C++: Aufbau


Kapitel 4: Überladen von Operatoren


Inhaltsverzeichnis

Dieses Buch ist unter einer Creative Commons-Lizenz lizensiert.


4.1 Allgemeines

Intuitiver Code

Unter Überladen von Operatoren versteht man das Anpassen der Funktionsweise von Operatoren für eigene Datentypen. Wenn Sie bisher Klassen definiert haben und Objekte basierend auf diesen erstellt haben, konnten Sie lediglich Methoden aufrufen, um mit den Objekten zu arbeiten. Objekte beispielsweise mit dem Operator + zu verknüpfen war nicht möglich.

Für einen lesbaren und einfacheren Code und für eine unkomplizierte Anwendung Ihrer Klassen zum Beispiel auch durch andere Programmierer kann es empfehlenswert sein, Operatoren für eigene Klassen zu überladen. Die Anwendung von Operatoren und das Erkennen ihrer speziellen Funktion kann einfacher sein als das Nachschlagen von Methoden und derer genauen Funktionsweise. Verkompliziert das Überladen von Operatoren eine Klasse ein wenig, so profitieren Sie später von den Operatoren bei der Anwendung der Klasse.


4.2 friend-Funktionen

Operatoren als globale Funktion überladen

Sie haben bereits in unzähligen Beispielen den Operator << verwendet, um Variablen auf den Bildschirm auszugeben. Wenn Sie sich folgendes Beispiel ansehen, so stellen Sie fest, dass Sie mit diesem Operator Variablen unterschiedlicher Datentypen auf den Bildschirm ausgeben können.

#include <string> 
#include <iostream> 

int main() 
{ 
  int i = 2; 
  std::string s = "Hallo"; 
  std::cout << i << s << std::endl; 
} 

Wenn Sie Variablen, die auf Ihren eigenen Datentypen basieren, auf die gleiche Weise auf den Bildschirm ausgeben möchten, dann stellen Sie spätestens bei der Kompilierung fest, dass das nicht funktioniert. Was spielt sich also eigentlich genau ab, wenn Variablen mit << an std::cout weitergegeben werden?

Operatoren sind genaugenommen nichts anderes als Funktionen oder Methoden. Für intrinsische Datentypen wie int oder float sind Operatoren in der Programmiersprache C++ ganz klar definiert. Für alle anderen Datentypen müssen Operatoren speziell definiert werden, damit klar ist, was bei einem Einsatz von Operatoren in Zusammenhang mit Objekten nicht-intrinsischer Datentypen geschehen soll.

Für nicht-intrinsische Datentypen sind Operatoren schlichtweg Methodenaufrufe. Wenn Sie Variablen mit << an std::cout übergeben, wird eine Methode operator<<() für das Objekt std::cout aufgerufen und das, was rechts vom binären Operator << steht, als einziger Parameter an diese Methode übergeben. Wenn diese Methode in der Klasse definiert ist, die natürlich auch einen Parameter vom richtigen Datentyp erwarten muss, dann kann der Compiler den Code übersetzen und eine ausführbare Datei ohne Probleme erstellen.

Das Beispiel von oben könnte auch wie folgt geschrieben werden.

#include <string> 
#include <iostream> 

int main() 
{ 
  int i = 2; 
  std::string s = "Hallo"; 
  std::cout.operator<<(i).operator<<(s).operator<<(std::endl); 
} 

Was müssen Sie nun machen, damit Sie Objekte Ihrer eigenen Datentypen an std::cout per Aufruf von << übergeben können? Sie müssen lediglich die Klasse, auf der std::cout basiert, um eine Methode << erweitern, die als einzigen Parameter ein Objekt Ihres eigenen Datentypen erwartet.

Da haben Sie jedoch leider schon ein Problem: Die Klasse, auf der std::cout basiert, gehört Ihnen nicht. Es handelt sich hierbei um die Klasse std::ostream, die in der Header-Datei iostream definiert ist. Die Klasse std::ostream ist Bestandteil des offiziellen C++-Standards, und verständlicherweise sollten Sie standardisierten Code nicht ändern.

Wie können Sie nun dennoch Variablen, die auf Ihrem eigenen Datentyp basieren, direkt über << an std::cout weitergeben, wenn Sie die Klasse std::ostream nicht anpassen können und somit keine Methode zur Verfügung steht, die genau einen Parameter Ihres Datentyps erwartet?

Wenn keine Methode gefunden wurde - es also keinen überladenen Operator in einer Klasse gibt, der den verwendeten Datentyp als Parameter erwartet - wird nach einer globalen Funktion gesucht. Dieser globalen Funktion werden zwei Parameter übergeben, nämlich die beiden Operanden des binären Operators.

Sehen Sie sich folgendes Beispiel an. Zuerst wird eine einfache Klasse mann definiert.

#include <string> 

class mann 
{ 
  public: 
    mann(std::string name) 
      : Name(name) 
    { 
    } 

  private: 
    std::string Name; 
}; 

Um möglichst einfach Objekte vom Datentyp mann überprüfen zu können, möchten Sie sie wie folgt auf den Bildschirm ausgeben können.

#include "mann.h" 
#include <iostream> 

int main() 
{ 
  mann m("Meier"); 
  std::cout << m << std::endl; 
} 

Wenn Sie obigen Code zu übersetzen versuchen, gibt der Compiler eine Fehlermeldung aus. Sie wissen ja auch inzwischen warum: Es wird versucht, eine Methode operator<<() für das Objekt std::cout aufzurufen und ihr einen Parameter vom Typ mann zu übergeben. Diese Methode ist natürlich in der Klasse std::ostream, auf der std::cout basiert, nicht vorhanden.

Wenn eine Methode in einer Klasse nicht gefunden wird, wird als nächstes versucht, eine globale Funktion operator<<() aufzurufen. Wenn Sie diese Funktion nun definieren, dann können Sie, wie Sie nachher sehen werden, tatsächlich Variablen vom Typ mann direkt auf den Bildschirm ausgeben.

Die globale Funktion zur Datenausgabe von Variablen vom Typ mann auf den Bildschirm sieht wie folgt aus.

#include "mann.h" 
#include <iostream> 

std::ostream &operator<<(std::ostream &ostr, mann &m) 
{ 
  return ostr << m.Name; 
} 

Wenn Sie einen Operator überladen - also eine Methode oder Funktion definieren, die einem Operator-Aufruf entspricht - geben Sie zuerst operator und dahinter ohne durch Leerzeichen getrennt das Operator-Zeichen an. Das ist der Methoden- bzw. Funktionsname. Alles, was Sie davor angeben, ist wie gewohnt der Rückgabewert der Methode bzw. der Funktion. Alles, was Sie dahinter angeben, ist wie eh und je die Parameterliste, die die Methode bzw. die Funktion erwartet.

Im Beispiel handelt es sich um eine Funktion, deren Name operator<<() lautet. Diese Funktion erwartet zwei Parameter: Eine Variable vom Typ std::ostream und eine Variable vom Typ mann. Bei der Anwendung des Operators << wird der erste Operand als erster Parameter an die Funktion übergeben, der zweite Operand als zweiter Parameter.

Der Rückgabewert der Funktion ist identisch mit dem Datentyp des ersten Parameters: Es wird auch tatsächlich in der Funktion mit return das Objekt zurückgegeben, das als erster Parameter an die Funktion übergeben wurde. Dies geschieht aus dem gleichen Grund, aus dem auch der Zuweisungsoperator eine Referenz auf das eigene Objekt zurückgibt: Dadurch sind Verkettungen, also die mehrmalige Anwendung eines Operators in einer Zeile möglich.

Was passiert in der Funktion operator<<()? Es wird auf die Eigenschaft Name des Parameters m zugegriffen und diese Eigenschaft an std::cout weitergegeben. Das ist insofern kein Problem, als dass der Datentyp std::string der Eigenschaft Name im offiziellen C++ Standard definiert ist - deswegen steht ihm ja das Kürzel std vor. Die Klasse std::ostream, auf der das Objekt std::cout basiert, definiert eine Methode operator<<(), die genau einen Parameter vom Typ std::string erwartet.

Dennoch kann die Funktion operator<<() so wie jetzt angegeben nicht funktionieren. Immerhin handelt es sich um eine freistehende Funktion, die versucht, auf eine private Eigenschaft der Klasse mann zuzugreifen. Und das ist nicht erlaubt.

Es gibt nun mehrere Möglichkeiten, diesen Zugriff einzuräumen: Die Eigenschaft Name kann öffentlich gemacht werden, oder man definiert eine öffentliche Methode in der Klasse mann, die den Wert der Eigenschaft Name zurückgibt. Der dritte Weg, den man normalerweise beim Überladen von Operatoren geht, sieht so aus, dass man die freistehende Funktion operator<<() zur friend-Funktion der Klasse mann macht. Im Code sieht das wie folgt aus.

#include <string> 
#include <iostream> 

class mann 
{ 
  public: 
    mann(std::string name) 
      : Name(name) 
    { 
    } 

    friend std::ostream &operator<<(std::ostream &ostr, mann &m); 

  private: 
    std::string Name; 
}; 

Es wird innerhalb der Klasse mann die freistehende Funktion deklariert und außerdem das Schlüsselwort friend davorgesetzt. Indem die Funktion operator<<() zur friend-Funktion gemacht wird, bekommt sie die gleichen uneingeschränkten Zugriffsrechte auf die Merkmale einer Klasse wie jede andere Methode der Klasse mann auch. Das heißt, innerhalb der Funktion operator<<() kann nun auf eine private Eigenschaft eines Objekts vom Typ mann zugegriffen werden - der Code kompiliert nun.

Wann sind friend-Funktionen von Nutzen? Wenn eine Funktion auf Daten in einer Klasse zugreifen muss, die mit dem Zugriffsattribut private geschützt sind, kann die Funktion mit friend deklariert werden, damit die Klasse nicht um eine öffentliche Methode ergänzt werden muss, die eigentlich - was die Klasse an sich und den Umgang mit ihr angeht - gar nicht benötigt wird. Anstatt also die Klasse um eine zusätzliche öffentliche Methode zu erweitern, die wohlmöglich von anderen Entwicklern eines Tages verwendet wird, obwohl sie gar nicht für einen direkten Aufruf durch andere Entwickler gedacht wurde, können Funktionen mit friend deklariert werden, die genau die gleichen Zugriffsrechte haben wie jede andere Methode einer Klasse.

Übrigens: Da friend-Funktionen keine Methoden sind und nicht zur Klasse gehören, werden sie nicht vererbt.


4.3 Überladen mit Methoden

Operatoren als Methoden überladen

Sie haben nun im vorherigen Abschnitt gesehen, wie friend-Funktionen eingesetzt werden, um Operatoren zu überladen. friend-Funktionen werden jedoch nur dann eingesetzt, wenn Sie keine Möglichkeit haben, den Operator in Form einer Methode in einer Klasse zu überladen. friend-Funktionen stellen also nur die zweitbeste Lösung dar. Wie die erstbeste Lösung aussieht, soll Ihnen im Folgenden gezeigt werden.

#include <string> 

class frau 
{ 
  public: 
    frau(std::string name) 
      : Name(name) 
    { 
    } 

  private: 
    std::string Name; 
}; 

Obige neue Klasse namens frau unterscheidet sich ausschließlich im Klassennamen von mann. In einem Programm soll es nun möglich sein, Objekte vom Typ mann und frau mit Hilfe des Operators + zu verknüpfen. Diese Verknüpfung symbolisiert eine Heirat, bei der Mann und Frau einen Doppelnamen erhalten, der aus den beiden vorherigen Namen besteht.

Die Anwendung soll also wie folgt aussehen.

#include "mann.h" 
#include "frau.h" 

int main() 
{ 
  mann m("Meier"); 
  frau f("Huber"); 
  m + f; 
} 

Nach dem Aufruf des Operators + soll in beiden Objekten als Name Meier-Huber gespeichert sein.

Damit das Programm funktioniert, muss der Operator + überladen werden. Sie könnten wieder eine globale Funktion definieren, die zwei Parameter vom Typ der beiden Operanden erwartet. Nachdem Sie jedoch Zugriff auf die Klasse des ersten Operanden haben, brauchen Sie nicht den Umweg über eine globale friend-Funktion gehen, sondern können den Operator + als Methode überladen.

Die Klasse mann wird um einen überladenen Operator erweitert, der einen Parameter vom Typ der Klasse frau erwartet.

#include <string> 
#include "frau.h" 

class mann 
{ 
  public: 
    mann(std::string name) 
      : Name(name) 
    { 
    } 

    void operator+(frau &f) 
    { 
      Name += "-" + f.Name; 
      f.Name = Name; 
    } 

  private: 
    std::string Name; 
}; 

Der Operator + wird wie << im Abschnitt zuvor überladen: Es wird das Schlüsselwort operator direkt gefolgt von + angegeben. Dies ist der Methodenname. Der Rückgabewert in diesem Fall ist void, und als einziger Parameter wird eine Referenz auf ein Objekt vom Typ frau erwartet. Ob der Parameter als Referenz übergeben wird oder nicht ist grundsätzlich egal und nur in diesem Beispiel wichtig.

Innerhalb der Methode operator+() wird zuerst zum Namen ein Bindestrich und dann der Name der Frau hinzugefügt. Außerdem wird der Name des Objekts vom Typ frau neu gesetzt, indem ihm der neue zusammengesetzte Doppelname des Mannes gegeben wird.

Hier gibt es jedoch wieder ein Problem: Die Eigenschaft Name in der Klasse frau ist privat. Die Klasse mann hat weder eine Schreib- noch eine Leseberechtigung und kann nicht auf diese geschützte Eigenschaft im übergebenen Parameter zugreifen.

Sie können nun entweder in der Klasse frau öffentliche Methoden implementieren, über die auf die Eigenschaft zugegriffen und sie gelesen und auf einen neuen Wert gesetzt werden kann. Oder Sie ändern das Zugriffsattribut für die Eigenschaft Name in der Klasse frau auf public, was jedoch grundsätzlich die schlechteste Lösung ist. Die dritte Möglichkeit ist, dass die Klasse frau die Klasse mann zum friend deklariert und somit weitergehende Zugriffsrechte einräumt.

Um die Klasse mann als friend zu deklarieren, müssen Sie die Klasse frau wie folgt ändern.

#include <string> 

class frau 
{ 
  friend class mann; 

  public: 
    frau(std::string name) 
      : Name(name) 
    { 
    } 

  private: 
    std::string Name; 
}; 

Wo genau in der Klasse die friend-Deklaration steht spielt keine Rolle. Es handelt sich hierbei nicht um ein Merkmal, mit dem sich in irgendeiner Weise arbeiten läßt und auf das ein Zugriffsattribut Anwendung findet.

In einem letzten Schritt wird die überladene Methode operator+() so abgeändert, dass sie den Doppelnamen zurückgibt. Dies geschieht nur, damit der neue Name später in der Funktion main() auf den Bildschirm ausgegeben werden kann, um zu überprüfen, ob der überladene Operator wie gewünscht funktioniert.

#include <string> 
#include "frau.h" 

class mann 
{ 
  public: 
    mann(std::string name) 
      : Name(name) 
    { 
    } 

    std::string operator+(frau &f) 
    { 
      Name += "-" + f.Name; 
      f.Name = Name; 
      return Name; 
    } 

  private: 
    std::string Name; 
}; 

Innerhalb von main() wird der Rückgabewert des überladenen Operators nun wie folgt auf den Bildschirm ausgegeben.

#include "mann.h" 
#include "frau.h" 
#include <iostream> 

int main() 
{ 
  mann m("Meier"); 
  frau f("Huber"); 
  std::cout << m + f << std::endl; 
} 

Wenn Sie obiges Programm ausführen, erscheint der neue Name Meier-Huber auf dem Bildschirm.


4.4 Unäre Operatoren

Überladen von Operatoren mit einem Operanden

Bisher haben Sie in diesem Kapitel gesehen, wie die Operatoren << und + überladen werden. Dabei handelte es sich um binäre Operatoren, also Operatoren, die zwei Operanden erwarten.

Das Überladen unärer Operatoren ist einfacher. Sie überladen unäre Operatoren immer durch Methodendefinitionen. Sie müssen sich also nicht damit rumschlagen, ob Methode oder freistehende Funktion der richtige Weg ist und Sie eventuell noch irgendwo das Schlüsselwort friend verwenden müssen. Sie überladen einen unären Operator, indem Sie eine Methode definieren - und fertig. Die genaue Vorgehensweise soll Ihnen anhand der Klasse frau gezeigt werden.

#include <string> 
#include "frau.h" 

class frau 
{ 
  public: 
    frau(std::string name) 
      : Name(name), Schwanger(false) 
    { 
    } 

    bool operator!() 
    { 
      Schwanger = true; 
      return Schwanger; 
    } 

  private: 
    std::string Name; 
    bool Schwanger; 
}; 

Es soll nun nicht mehr nur möglich sein, mit dem Operator + zu heiraten. Die Frau soll außerdem mit dem Operator ! schwanger werden können. Hierbei handelt es sich um einen unären Operator, so dass nun eine Methode in der Klasse frau definiert werden muss.

Die Definition einer Methode zum Überladen eines unären Operators sieht bekannt aus: Es wird das Schlüsselwort operator angegeben gefolgt vom Operatorzeichen !. Nachdem es sich um einen unären Operator handelt, wird und darf kein Parameter dieser Methode übergeben werden. Der Rückgabewert kann von Ihnen frei gewählt werden. Wichtig ist nur, dass die Parameterliste für unäre Operatoren leer ist.

Bei Verwendung des Operators ! soll die Frau schwanger werden. Dafür wird eine Variable vom Typ bool der Klasse frau hinzugefügt, die angibt, ob die Frau schwanger ist oder nicht. Beim Erstellen eines Objektes vom Typ frau wird die bool-Variable standardmäßig auf false gesetzt. Wird der Operator ! auf ein Objekt vom Typ frau angewandt, wird die bool-Variable auf true gesetzt. Zur Überprüfung wird der Wert außerdem zurückgegeben.

#include "mann.h" 
#include "frau.h" 
#include <iostream> 

int main() 
{ 
  mann m("Meier"); 
  frau m("Huber"); 
  std::cout << m + f << std::endl; 
  std::cout << !f << std::endl; 
} 

Im obigen Programm wird der Operator ! so angewandt, wie Sie es von intrinsischen Variablen bereits gewohnt sind. Indem ! vor das Objekt gestellt wird, wird die Methode operator!() aufgerufen.


4.5 Zusammenfassung

Überladen von Operatoren im Überblick

Das Überladen von Operatoren folgt eigentlich recht einfachen Regeln, die Ihnen in diesem Kapitel erklärt werden sollten. Um sich schneller in diesem Regelwerk zurechtzufinden und es besser zu überblicken, soll nochmal in aller Kürze das Wichtigste zum Überladen von Operatoren zusammengefasst werden.

Wird ein Operator im Zusammenhang mit mindestens einem nicht-intrinsischen Datentyp verwendet, handelt es sich um einen überladenen Operator. Der C++-Compiler versucht in diesem Fall zuerst eine Methode für das Objekt zu finden, das erster Operand ist. Ist in der Klasse, auf der der erste Operand basiert, keine entsprechende Methode definiert, wird nach einer freistehenden Funktion gesucht, der die beiden Operanden als Parameter übergeben werden können.

Sie als Programmierer versuchen grundsätzlich, überladene Operatoren als Methoden zu implementieren. Gelingt dies nicht, weil Sie nicht Zugriff auf die Klasse des ersten Operanden haben, überladen Sie den Operator als freistehende Funktion. Oft ist es dann notwendig, die freistehende Funktion als friend-Funktion in der Klasse des zweiten Operanden zu deklarieren, um Zugriff auf geschützte Merkmale zu erhalten.

Das Überladen von unären Operatoren, das Sie im vorherigen Abschnitt kennengelernt haben, läuft immer über die Definition von Methoden und niemals über freistehende Funktionen.

Beachten Sie außerdem: Nur weil Sie nun wissen, wie Operatoren überladen werden können, heißt das nicht, dass Sie ab sofort wie wild Operatoren für Ihre Klassen definieren. Operatoren sollten da eingesetzt werden, wo sie schnell und einfach eine Funktion sichtbar machen können. Der überladene Operator + in diesem Kapitel ist ein gutes Beispiel, da er Mann und Frau verknüpft und so der eigentlichen Bedeutung Heirat sehr nahe kommt. Ob der unäre Operator ! hier jedoch so sinnvoll eingesetzt wurde, darf bezweifelt werden. Kann ein anderer Programmierer wohl recht schnell die Assoziation zwischen + und Heirat herstellen und sie sich gut merken, so fällt dies bei ! und Schwangerschaft doch sehr viel schwerer. Sie sollten daher immer versuchen, die ursprüngliche Bedeutung der Operatoren für intrinsische Datentypen im Zusammenhang mit Ihren Klassen nicht völlig neu zu definieren. Je näher Sie sich an der Original-Bedeutung orientieren, umso einleuchtender ist die Verwendung von überladenen Operatoren.

Überladene Operatoren werden, wenn sie als Methode definiert sind, vererbt. Davon ausgenommen ist der Zuweisungsoperator, den Sie bereits kennengelernt haben. Es handelt sich hierbei zwar um einen überladenen Operator, nur ist dies eine spezielle Methode, für die andere Regeln gelten.


4.6 Aufgaben

Übung macht den Meister

Sie können die Lösungen zu allen Aufgaben in diesem Buch als ZIP-Datei erwerben.

  1. Ihre Aufgabe ist die Simulation eines in der Objektorientierung nachvollziehbaren Firmenzusammenschlusses. Erstellen Sie hierzu eine Klasse firma und überladen Sie den Operator + derart, dass die Namen zweier Firmen nach einem Zusammenschluss jeweils aus dem Doppelnamen der beiden Firmen bestehen. Definieren Sie eine Methode name(), die den Namen einer Firma als std::string zurückgibt. Erstellen Sie zum Test Ihrer Klasse firma zwei Firmen. Lassen Sie diese Firmen sich zusammenschliessen und geben Sie anschließend den Namen jeder Firma auf den Bildschirm aus.

    Damit sich zwei Firmen zusammenschließen können, müssen Sie zwei Objekte vom Typ firma mit dem Operator + verknüpfen. Das heißt, auch der erste Operand dieser Verknüpfung ist ein Objekt vom Typ firma. Nachdem Sie die Klasse selber erstellen sollen und Zugriff auf sie haben, können Sie den überladenen Operator + als Methode in der Klasse firma definieren.

  2. Die Klasse firma aus Aufgabe 1 soll nun so erweitert werden, dass sich zwei Firmen nicht nur mit Hilfe des Operators + zusammenschliessen können, sondern dass eine Firma mehrere andere Firmen übernehmen kann. Überladen Sie hierzu den Operator << derart, dass Sie die Übernahme mehrerer Firmen in einer einzigen Code-Zeile angeben können. Übernommene Firmen sollen innerhalb der Klasse firma in einem Array mit zehn Feldern vom Typ std::string gespeichert werden. Erweitern Sie außerdem die Klasse firma um eine Methode firmen(), die bei Aufruf die Namen übernommener Firmen auf den Bildschirm ausgibt. Erstellen Sie zum Test der Klasse firma drei Firmen, von denen eine die beiden anderen übernimmt. Die Übernahme beider Firmen muss in einer einzigen Code-Zeile angegeben werden. Geben Sie danach die Namen der übernommenen Firmen auf den Bildschirm aus.

    Sie müssen nun den Operator << für die Klasse firma überladen. Nachdem eine Firma eine andere Firma übernehmen soll und somit beide Operanden wieder Objekte vom Typ firma sind, können Sie auch in diesem Fall den Operator als Methode überladen. Hierbei ist nun wichtig, dass der überladene Operator als Rückgabewert den jeweils ersten Operanden als Referenz liefert - andernfalls ist es nicht möglich, innerhalb einer Zeile mehrere Objekte vom Typ firma mit << zu verknüpfen.

  3. Erweitern Sie die Klasse firma aus Aufgabe 2 dahingehend, dass bei einem Firmenzusammenschluss ein Konzern entsteht. Erstellen Sie hierzu eine neue Klasse konzern mit einer privaten Eigenschaft Name vom Typ std::string und einem geeigneten öffentlichen Konstruktor mit Möglichkeit zum Initialisieren der Eigenschaft. Die überladene Methode operator+() der Klasse firma soll ein Objekt vom Typ konzern zurückgeben, das den Namen beider sich zusammenschließender Firmen erhält. Erstellen Sie zum Test zwei Firmen, die sich zusammenschließen. Geben Sie den daraus resultierenden neuen Konzern direkt an die Standardausgabe weiter, so dass der Name des Konzerns auf den Bildschirm ausgegeben wird.

    Gehen Sie Schritt für Schritt vor: Bei einem Firmenzusammenschluss soll nun ein Objekt vom Typ konzern entstehen. Definieren Sie eine Klasse konzern, erstellen Sie im überladenen Operator + der Klasse firma ein Objekt vom Typ konzern und geben Sie dieses als Rückgabewert des überladenen Operators zurück. Damit dieses Objekt vom Typ konzern direkt an die Standardausgabe weitergereicht werden kann, müssen Sie den Operator << überladen - diesmal jedoch als Funktion. Denn der erste Operand, also std::cout, ist ein Objekt vom Typ std::ostream, und auf diese Klasse haben Sie keinen Zugriff. Sie können also die Klasse, auf der die Standardausgabe basiert, nicht anpassen, so dass Sie den Operator als globale Funktion überladen müssen.

  4. Aufgrund wettbewerbspolitischer Entscheidungen muss der Konzern, der in Aufgabe 3 aus dem Zusammenschluss zweier Firmen hervorging, zerschlagen werden. Überladen Sie hierzu den unären Operator - derart, dass einfach eine Ausgabe auf den Bildschirm erfolgt, dass der Konzern zerschlagen wird. Rufen Sie den überladenen Operator für den Konzern aus Aufgabe 3 auf und führen Sie das Programm zum Test aus.

    Sie müssen lediglich den Operator - für die Klasse konzern überladen. Das machen Sie, wie es sich für unäre Operatoren gehört, als Methode.