Kopierkonstruktor |
Wenn ein Objekt kopiert wird, wird der Speicher, den das Objekt einnimmt, Bit für Bit an die Zielstelle kopiert. In manchen Fällen führt das aber nicht zum gewünschten Ergebnis. Ein solcher Fall tritt immer dann ein, wenn die Klasse Zeiger enthält, die auf Daten außerhalb des Objektspeichers zeigen. Bei der Kopie würde der Zeiger mitkopiert. Aber die Daten, auf die der Zeiger verweist, werden nicht kopiert. Stattdessen weisen anschließend die Zeiger von Original und Kopie auf dieselbe Speicherstelle, da der Zeiger ja mitkopiert wurde. Das bedeutet, dass die Kopie des Objekts keine wirkliche Kopie ist, sondern über den Zeiger mit den Daten des Originals arbeitet. Abbildung (abbobjkopie) veranschaulicht diese Situation.
An dieser Stelle steht im Buch die Abbildung "Kopieren von Objekten" (abbobjkopie)
| Wann muss ein Kopierkonstruktor erstellt werden? |
| Wenn die Klasse Verweise auf fremden Speicher hat, muss ein Kopierkonstruktor erstellt werden. Solche Verweise sind Zeiger und Referenzen. Aber auch fremde Klassen können solch einen Verweis enthalten, wie beispielsweise Strings. |
Wenn Sie als Datenanteil in der Klasse tStack statt eines Integers einen Zeiger verwenden, dann würden Sie in der Klasse tStack einen Kopierkonstruktor benötigen. Um zu zeigen, wie dieser arbeiten müsste, erfinden wir hier eine sehr einfache Klasse mit einem externen Speicher.
[Externer Speicher und Kopierkonstruktor (copycons.cpp)]
// Programm zur Demonstration eines Kopierkonstruktors
#include <iostream>
using namespace std;
class tKlasse
{
public:
tKlasse() // Konstruktor: erzeugt externe Daten
{
Zeiger = new int;
*Zeiger = 5;
}
~tKlasse() // Destruktor: gibt externe Daten frei
{
delete Zeiger;
Zeiger = 0;
}
void SetData(int a) { *Zeiger = a; }
int GetData() { return *Zeiger; }
tKlasse(const tKlasse& k) // Kopierkonstruktor
{
// zur Demonstration meldet er sich
cout << "Kopierkonstruktor" << endl;
// Externe Daten erzeugen und kopieren
Zeiger = new int;
*Zeiger = k.GetData();
// Normale Datenelemente auch kopieren
sonstiges = k.sonstiges;
}
int sonstiges; // steht für die Nicht-Zeiger Datenelemente
private:
int *Zeiger; // Zeiger, also Kopierkonstruktor notwendig
};
// Die Funktion dient nur zur Demonstration. Weil der Parameter
// per Wert übergeben wird, wird beim Aufruf der
// Kopierkonstruktor aufgerufen
void Funktion(tKlasse para)
{
cout << "Funktion:" << para.GetData() << endl;
}
// Hauptprogramm zum Testen
int main()
{
tKlasse Objekt;
Objekt.SetData(7);
Funktion(Objekt); // Hier wird der Kopierkonstruktor aktiv
cout << Objekt.GetData() << endl;
}
Der Parameter des Kopierkonstruktors hat das Attribut const, da der Parameter kopiert und nicht etwa ver&uaml;ndert werden soll. Verändert wird das Objekt selbst, also das, auf das this zeigt.
Um zu zeigen, wo der Kopierkonstruktor eingesetzt wird, gibt er eine kurze Meldung auf dem Bildschirm aus. Wenn Sie den Kopierkonstruktor auskommentieren, wird nach dem Aufruf der Funktion der Datenbereich, auf den der Zeiger des Objektes zeigt, freigegeben sein. Wenn Sie dies nachprüfen wollen, ändern Sie den Destruktor so, dass er den externen Integerwert auf 9 setzt, anstatt ihn freizugeben.
~tKlasse() // Destruktor: hier zum Test missbraucht
{
*Zeiger = 9; // nur zum Test
}
Bei auskommentiertem Kopierkonstruktor meldet die letzte Zeile im Programm eine 9, als deutliches Zeichen, dass der Destruktor auf dem Bereich gearbeitet hat, auf den der Zeiger verweist. Wenn Sie den Kopierkonstruktor wieder aktivieren, erscheint wieder 7.
|
Diese Seite basiert auf Inhalten aus dem Buch
Arnold Willemer: Einstieg in C++ 3. Aufl. Mit freundlicher Genehmigung und Unterstützung des Verlags galileo computing |
| Informatik-Ecke Einstieg in C++ |
(C) Copyright 2008 Arnold Willemer
|