|
|
3. Abend |
|
Ziele : Wir können nach diesem Abend eine Klasse mit dynamischen Datenelementen definieren und dabei den Kopierkonstruktor brauchen, sowie den Zuweisungsoperator überschreiben.
|
|
Dynamische Elemente |
|
Was hier unten steht ist nur ein wenig Ergänzung zum Kapitel 22 im Buch ! |
|
Dynamische Elemente können in Klassen verwendet werden um Datenelemente von variabler Länge zu halten. Angenommen wir wollen eine Klasse schreiben, die als Datenelement einen "string" haben soll, also eine Kette von char's. Dabei soll es aber möglich sein diese Kette zur Laufzeit des Programmes beliebig zu erweitern. Wir verwenden ein Array, das zur Laufzeit verändert werden kann. Solche Klassen nennt man häufig Vektoren. Wenn wir hier so etwas wie einen Vektor mit char's schreiben ist das sozusagen unsere eigene string-Klasse. |
|
|
|
Hier die noch leblose .cpp Datei dazu |
|
|
|
Hervorheben möchte ich nur den Zeiger m_pChars, der auf Speicher zeigt, in dem wir unsere Daten speichern. Dieser Speicher wird mit new angefordert. Den Vorgang den Speicher mit new anzufordern nennt man "allozieren". |
|
Anforderungen |
|
Wir wollen Objekte unserer Klasse in einem ersten Schritt auf folgende Art verwenden können : |
|
|
|
Der Compiler lässt dies aber noch nicht zu. Wir müssen unserer
Klasse MyString erst die Fähigkeiten geben, die wir anweden wollen.
Wir brauchen also in unserer Klasse einen Konstruktor mit Parametern und
wir müssen den Zuweisungsoperator überschreiben. Schau nach,
wie wir in der letzten Lektion
bei der Klasse Complex den +-operator überschrieben haben. Genau
gleich können wir den =-operator (Zuweisungsoperator) überschreiben
! |
|
|
|
Implementation |
|
Ganz interessant wird jetzt das .cpp File dazu !!!! |
|
|
|
Kopierkonstruktor |
|
Der Kopierkonstruktor muss für Klassen mit dynamischen Elementen im allgemeinen auch selber erstellt werden. Auch in unserem Fall. Ergänzen wir das main von oben mit folgender Zeile und lassen das Programm laufen geschieht folgendes : |
|
|
|
Das geschieht, weil der vom Compiler automatisch erzeugte Kopierkonstruktor
(Standardkopierkonstruktor) macht, dass der Zeiger "m_pChars"
von "einString" gleich ist wie m_pChars des Objektes "Kopie".
Beide Destruktoren rufen auf : |
|
Wollen wir von unserem myString-Objekt eine saubere Kopie machen, müssen wir selber einen Kopierkonstruktor schreiben, der zuerst selber genügend Speicher alloziert, und dann den Inhalt der Daten kopiert ! Also ergänzen wir die Klasse "MyString" mit dem Kopierkonstruktor, hier die Zeile, die wir im public-Teil der Klasse MyString ergänzen. |
|
|
|
Hier der Code für den Kopierkonstruktor (für das .cpp-file). |
|
|
|
Beachte, wie auf das private Datenelement des anderen Objektes (hier c) zugegriffen wird. |
|
Zuweisungsoperator |
|
Wir haben bereits einen Zuweisungsoperator, mit dem wir unserem Objekt einen char* zuweisen können. Was wir auch können wollen ist das Zuweisen von einem MyString-Objekt an ein anderes MyString-Objekt. |
|
|
|
Auch dieser Code kompiliert problemlos ohne, dass wir einen speziellen Zuweisungsoperator schreiben müssen, denn auch hier erzeugt der Compiler einen Standardzuweisungsoperator. Auch dieser Kopiert nur den Wert des Zeigers m_pChars von "einString" zum Objekt "Gleich". Beide Objekte haben also einen Zeiger auf den gleichen dynamisch allozierten Speicher. Sobald eines der Objekte delete darauf aufruft hat das zweite Objekt einen Zeiger auf Speicher, der bereits freigegeben wurde. Jeglicher Zugriff darauf führt bestenfalls zu einer Schutzverletzung ! |
|
Schreiben wir also unseren eigenen Zuweisungsoperator ! |
|
|
|
Der Zuweisungsoperator hat als Rückgabewert eine Referenz auf ein MyString-Objekt. Der Operator gibt eine Referenz auf sich selbst zurück, damit Mehrfachzuweisungen wie diese möglich sind : |
|
|
|
Hier die Implementation aus dem .cpp-File |
|
|
|
In unserer Klasse MyString haben wir mehrmals ähnlichen Code geschrieben. Mehrmals haben wir den Code, der zuerst den alten Speicher löscht und dann vom anderen Objekt kopiert ! Könnte man diesen Code nur einmal haben ? |
|
Übung |
|
1. |
|
Vereinfache den in MyString.cpp ! Mach, dass der Codeblock mit delete,
strlen und strncpy nur einmal im ganzen file vorkommt. |
|
2. |
|
Schreibe einen operator+, der es zulässt einen Text an ein MyString-Objekt anzuhängen, so dass Code wie dieser richtig kompiliert wird : |
|
|
|
Tipp : |
|
|
|
2a. |
|
|
Erweitere die Aufgabe 2 so, dass auch folgender Code richtig funktioniert: |
|
|
Tipp: |
|
3. |
|
Definiere eine Funktion, mit der man MyString in einen ostream schicken
kann. Damit sollte die Ausgabe unserer "MyString"-Klasse möglich
sein. |
|
4. |
fakultativ |
Ganz Elegant wäre zur Ausgabe des Strings man könnte schreiben : |
|
|
|
Dazu muss man den <<-operator überschreiben ! |
|
|
|
Denn m_pChars ist ein privates Datenelement ! Wer jetzt hingeht und dieses
Datenelement public macht, wird auf der Stelle von seinem Computer
gefressen und von mir geächtet ! Das public machen von Datenelementen,
vor allem wenn es Zeiger auf dynamisch allozierten Speicher sind, ist
schlecht. Stellt Euch vor Ihr lasst die Hosen mitten in der Stadt einfach
fallen, so ist es ungefähr wenn man ein Datenelement public macht. |
|
|
|
Das heisst wir sagen dem Compiler, dass es irgendwo eine Funktion gibt die wie folgt aussieht und Zugriff auf die Datenelemente der Klasse MyString hat. Am besten wir machen die Funktion in die Datei MyString.cpp, obwohl die Funktion nicht wirklich zur Klasse MyString gehört. |
|
|
|
Die Lösung findet Ihr hier ! |