==================================================================== Trennung zwischen Speicherverwaltung und Auf- und Abbau von Objekten [TOC] ==================================================================== Unsere letzte Lösung hatte den Nachteil, dass zu den Template-Abhängigkeiten ein _default constructor_ für den Element-Typ gehörte und es unvermeidlich war, dass alle Objekte zunächst ohne Parameter konstruiert worden sind, bevor sie mit sinnvollem Inhalt gefüllt wurden. Prinzipiell ist es möglich, das Belegen und Freigeben von Speicher zu trennen von dem Konstruieren und Abbauen eines Objekts. Angenommen, _T_ ist der Elementtyp und _size_ die Zahl der gewünschten Elemente, dann können die vier genannten Operationen wie folgt durchgeführt werden: * Belegen von Speicher: ---- CODE (type=cpp) ----------------------------- static_cast(operator new(sizeof(T) * size)) -------------------------------------------------- Mit `operator new` wird Speicher belegt, ohne das der Speicher irgendwie befüllt wird, es werden also keine Objekte konstruiert. Hier ist die Größe der gewünschten Speicherfläche in Bytes anzugeben, weswegen `sizeof(T)` mit `size` multipliziert werden muss, um die Gesamtgröße korrekt zu ermitteln. Normalerweise läuft dies auf `std::malloc` hinaus, aber anders als `std::malloc` kann `operator new` bei Bedarf überdefiniert werden. Wie `std::malloc` liefert auch der `operator new` einen Zeiger des Typs `void*` zurück, der mit Hilfe des `static_cast`-Operators in den passenden Zeigertyp konvertiert werden muss. * Konstruktion eines Objekts auf bereits belegtem Speicher: ---- CODE (type=cpp) ----------------------------- // Annahme: data ist vom Typ T* new (data + index) T(other.data[index]); -------------------------------------------------- In dieser Form des `new`-Operators wird in Klammern zuerst die Adresse angegeben, wo ein Objekt zu konstruieren ist. Danach folgt der Datentyp _T_ und der oder die Parameter eines Konstruktors von _T_, wobei hier ein Kopierkonstruktor zum Einsatz gelangt. * Abbau eines Objekts, ohne den zugehörigen Speicherplatz freizugeben: ---- CODE (type=cpp) ----------------------------- data[index].~T(); -------------------------------------------------- Der _destructor_ kann explizit aufgerufen werden. In diesem Fall wird das Objekt nur abgebaut, ohne den zugehörigen Speicher freizugeben. * Freigabe von Speicher: ---- CODE (type=cpp) ----------------------------- operator delete(data) -------------------------------------------------- Aufgaben ======== * Passen Sie Ihre Array-Template-Klasse dahingehend an, dass das Belegen und Freigaben des Speichers getrennt wird von dem Auf- und Abbau der Elemente. Der Kopierkonstruktor sollte entsprechend alle Elemente kopierkonstruieren können, ohne den _default constructor_ zu verwenden. Der reguläre Konstruktor darf aber weiterhin die einzelnen Elemente mit dem _default constructor_ konstruieren. Testen Sie diese Fassung mit dem bereits existierenden Testprogramm unter Einsatz von _valgrind_. * Fügen Sie einen Konstruktor hinzu, der neben der Dimension noch eine `const`-_lvalue_-Referenz des Typs _T_ erhält. Dieses Element dient dann als Kopiervorlage für alle im Array anzulegenden Elemente. Auf diese Weise ist die Anlage eines Arrays auch ohne einen _default constructor_ für den Elementtyp möglich. Testen Sie den neuen Konstruktor mit einem Elementtyp ohne _default constructor_. Sie können hierzu `test4.cpp` verwenden und müssten es entsprechend anpassen: :import: session04/step01/test4.cpp [fold] Wie Sie daran erkennen können, ist es ein Vorteil, dass nicht sämtliche Template-Abhängigkeiten für alle Methoden einer Klasse erfüllt sein müssen, solange wir mit den Abhängigkeiten auskommen der Methoden, die wir tatsächlich benötigen. :navigate: up -> doc:index back -> doc:session04/page02 next -> doc:session04/page04