C++ Kurs

Überladen des Konstruktors

Die Themen:

Überladen des Konstruktors
Kopierkonstruktor
Beispiel und Übung

Überladen des Konstruktors

Nachdem Sie in der letzten Lektion etwas über das allgemeine Überladen von Funktionen bzw. Memberfunktionen erfahren haben, sehen wir uns jetzt das Überladen des Konstruktors an.

Hatten unsere Klassen bisher immer nur einen Konstruktor, so ermöglicht das Überladen des Konstruktors, dass ein Objekt einer Klasse auf verschiedene Weisen definiert werden kann. Und auch für das Überladen des Konstruktors gilt: die Konstruktore müssen sich in der Anzahl der Parameter und/oder Datentypen der Parameter unterscheiden. Welcher Konstruktor dann ausgeführt wird, hängt von den Parametern bei der Definition des Objekts ab.


class Window
{
    ....
  public:
    Window();                   // Standard-ctor
    Window(char*, short);       // 2. ctor
    ....
};
int main()
{
    Window myWin;               // Standard-ctor
    Window yourWin("Emil",10);  // 2. ctor
    ....
}

Beachten Sie in diesem Zusammenhang auch, dass Sie den Standard-Konstruktor immer benötigen, wenn Sie Objektfelder dynamisch definieren wollen.

Jede Klasse kann aber nur einen Destruktor besitzen, da der Destruktor immer parameterlos ist!

Kopierkonstruktor

Eine besondere Form des Konstruktors stellt der Kopierkonstruktor (copy-ctor) dar. Er wird immer dann aufgerufen, wenn ein Objekt bei seiner Definition mit einem anderen Objekt der gleichen Klasse initialisiert wird, d.h. das neue Objekt eine Kopie eines bestehenden Objekts ist. Der Kopierkonstruktor hat folgende Syntax:

CAny::CAny(const CAny& source);

CAny ist eine beliebige Klasse und der Referenzparameter source eine Referenz auf das zu kopierende Objekt. Nachfolgend ein Beispiel für einen solchen Kopierkonstruktor.


// Klassendefinition
class Window
{
    char *pTitle;
    ....
  public:
    Window();                       // Standard ctor
    Window(const Window& source);   // copy-ctor
    ....
};
// Definition des Kopierkonstruktors
Window::Window(const Window& source)
{
    // Platz für Fenstertitel reservieren
    pTitle = new char[strlen(source.pTitle)+1];
    // Fenstertitel umkopieren
    strcpy(pTitel,source.pTitle);
    ....
}

int main()
{
    // Aufrufe des Standard-ctor
    Window firstWin;
    pAnyWin = new Window;
    // Beispiele für den Aufruf des copy-ctors
    Window myWin(firstWin);
    Window yourWin(*pAnyWin);
    Window *pWin1 = new Window(firstWin);
    Window *pWin2 = new Window(*pAnyWin);
    ...
    ...  // Hier dyn. angelegte Window-Objekte wieder löschen!
}

Beachten Sie bitte bei den Zeigerparametern, dass die Zeiger dereferenziert werden!

Selbstverständlich sollten Sie in der Praxis für die Ablage des Fenstertitels ein Objekt von Typ string verwenden. Die Ablage des Fenstertitels in einem char-Feld dient hier nur zur Veranschaulichung wie ein Kopierkonstruktor generell aufgebaut ist.

Der Kopierkonstruktor ist in der Regel immer dann erforderlich, wenn eine Klasse dynamische Eigenschaften enthält, da meistens nicht einfach die Zeiger auf die dynamische Eigenschaften kopiert werden dürfen (doppelt belegter Speicherplatz!).

Wollen Sie aufgrund irgend welchen Anforderungen verhindern, dass von bestimmten Objekten Kopien erzeugt werden können, so deklarieren Sie den Kopierkonstruktor als private.

Beispiel und Übung

Beispiel:

Dieses Beispiel entspricht in seine Funktionalität dem Beispiel aus der vorherigen Übung (Abspeichern von Rechteckdaten).

Im Beispiel wird eine Klasse für ein Rechteck-Objekt definiert. Das Rechteck besitzt auch hier die üblichen Eigenschaften wie Position und Größe. Das Setzen der Rechteckdaten erfolgt nun aber im Gegensatz zum vorherigen Beispiel im Konstruktor. Der erste Konstruktor erhält die Rechteckdaten als short-Werte übergeben, während der zweite Konstruktor die Eigenschaften eines als Parameter übergebenen Rechtecks verwendet (Kopierkonstruktor). Zur Ausgabe der Rechteckdaten dient wieder die Memberfunktion PrintRect(...).

In main() wird dann ein erstes Rechteck dynamisch erstellt, wobei die Rechteckdaten als short-Werte übergeben werden. Danach wird ein zweites Rechteck dynamisch erstellt, das nun mit den Eigenschaften des ersten Rechtecks initialisiert wird. Zur Kontrolle werden die Daten der beiden Rechtecke ausgegeben.

Zum Schluss nicht vergessen, beide Rechtecke auch wieder zu löschen!

1. Rechteck:
Rechteck auf (10,20) Grösse: (100,200)
2. Rechteck:
Rechteck auf (10,20) Grösse: (100,200)


// Beispiel zum überladenen Konstruktor

// Zuerst Dateien einbinden
#include <iostream>

using std::cout;
using std::endl;

// Klassendefinition
class Rect
{
    short   xPos, yPos;             // Position
    short   width, height;          // Breite und Höhe
  public:
    // 1. Konstruktor
    Rect(short x, short y, short w, short h);
    // 2. Konstruktor (Kopierkonstruktor)
    Rect(const Rect& source);
    // Ausgabe der Rechteck Daten
    void PrintRect() const;
};
// Definition der Memberfunktionen
// 1. Konstruktor
// Erhält als Parameter die Rechteck-Daten als 4 short Werte
Rect::Rect(short x, short y, short w, short h)
{
    xPos = x; yPos = y;
    width = w; height = h;
}
// 2. Konstruktor (Kopierkonstruktor)
Rect::Rect(const Rect& source)
{
    xPos = source.xPos; yPos = source.yPos;
    width = source.width; height = source.height;
}
// Ausgabe der Rechteck Daten
void Rect::PrintRect() const
{
    cout << "Rechteck auf (" << xPos << "," << yPos << ") ";
    cout << "Grösse: (" << width << "," << height << ")\n";
}

// main() Funktion
int main()
{
    // Zeiger auf erste Rect Objekt
    Rect   *pFirstRect;

    // Erstes Rect Objekt erstellen
    pFirstRect = new Rect(10,20,100,200);
    // Zweites Rect Objekt erstellen und mit den Eigenschaften
    // der ersten Rect Objekts initialisieren
    Rect *pSecondRect = new Rect(*pFirstRect);

    // Rechteckdaten ausgeben
    cout << "1. Rechteck:\n";
    pFirstRect->PrintRect();
    cout << "2. Rechteck:\n";
    pSecondRect->PrintRect();

    // Beide Objekte wieder löschen
    delete pFirstRect;
    delete pSecondRect;
}

Übung:

Schreiben Sie die in der letzten Übung erstellte Klasse CString nun so um, dass die beiden Memberfunktionen SetString(...) durch entsprechende Konstruktore ersetzt werden. Die Klasse sollte dann folgende Konstruktore enthalten:

  • Den Standard-Konstruktor für ein leeres CString Objekt
  • Einen Konstruktor der den zu setzenden String als char-Zeiger erhält
  • Einen Kopierkonstruktor

Die anderen Memberfunktionen AddString(...) und PrintString(...) können Sie unverändert übernehmen.

Erstellen Sie in main() ein erstes CString-Objekt, das Sie mit dem C-String "Es waren " initialisieren und ein zweites leeres CString-Objekt. Geben Sie beide CString-Objekte aus.

Fügen Sie dann zum ersten CString-Objekt den C-String "zwei Ameisen" und zum zweiten CString-Objekt den C-String "die wollten nach Amerika reisen" hinzu. Geben Sie beide CString-Objekte wieder aus.

Zum Schluss definieren Sie ein drittes CString-Objekt, das Sie mit dem Inhalt des ersten CString-Objekts initialisieren. Diesem neuen CString-Objekt fügen Sie dann den C-String "\n" so wie das zweite CString-Objekt hinzu. Geben Sie auch dieses Objekt wieder aus.

Ausgangs-Strings
1.String: Es waren
2.String:

Nach AddString(...)
1.String: Es waren zwei Ameisen
2.String: die wollten nach Amerika reisen

Neuer String
3.String: Es waren zwei Ameisen
die wollten nach Amerika reisen

Lösung ansehen!