================== Generische Klassen [TOC] ================== Nehmen wir als Beispiel folgende Klasse _Matrix_ für Matrizen, die bislang nur für den Elementtyp _double_ ausgelegt ist: :import: session04/matrix-double.hpp Wenn wir gerne eine ähnliche Matrix-Klasse für den Datentyp _float_ hätten, könnten wir den obigen Programmtext kopieren und im gesamten Text _double_ durch _float_ ersetzen. Dies könnte dann klappen, wenn wir anschließend der Matrix-Klasse einen neuen Namen geben würden, um ein Namenskonflikt zu vermeiden. Die textuelle Replikation von Programmtexten mit minimalen Variationen ist kein guter Ansatz aus der Sicht des Software Engineering. Wenn wir die originale Fassung verbessern, müssten wir diese Änderungen manuell bei all den anderen Varianten nachziehen. Seit den 1970er-Jahren gibt es Programmiersprachen, die die generische Programmierung unterstützen (die erste solche Sprache war CLU, die von Barbara Liskov am MIT entwickelt worden ist). Eine generische Klasse hängt von einem oder mehreren Typparameter ab und kann in generischer Form unter Nutzung dieser Typparameter geschrieben werden. Solche generische Klassen können dann mit konkreten Datentypen instantiiert und danach wie jede andere Klasse genutzt werden. Generische Konstrukte werden in C++ Templates genannt. Templates können sich in C++ auf Klassen, Funktionen und einige andere Deklarationen beziehen. Im Falle einer Matrix-Klasse würden wir hier nur einen einzigen Parameter _T_ für den Elementtyp benötigen: ---- CODE (type=cpp) ---------------------------------------------------------- template struct Matrix { const std::size_t m; /* number of rows */ const std::size_t n; /* number of columns */ const std::size_t incRow; /* stride between subsequent rows */ const std::size_t incCol; /* stride between subsequent columns */ T* data; /* ... */ T& operator()(std::size_t i, std::size_t j) { assert(i < m && j < n); return data[i*incRow + j*incCol]; } }; ------------------------------------------------------------------------------- Eine Template-Deklaration * beginnt mit dem Schlüsselwort _template_, * benennt alle Template-Parameter innerhalb von winkligen Klammern, bei denen Typparameter mit dem Schlüsselwort _typename_ gekennzeichnet werden und * wird mit einer regulären Deklaration abgeschlossen, bei der die Template-Parameter frei genutzt werden können. Im obigen Beispiel ist _Matrix_ nicht mehr länger eine gewöhnliche Klasse, sondern eine Template-Klasse. Template-Klassen können nur genutzt werden, wenn sie zuvor instantiiert wurden, d.h. die Template-Parameter müssen mit konkreten Typen assoziiert werden. Das kann geschehen, indem die gewünschen Parameter ebenfalls in winkligen Klammern angegeben werden. Im folgenden Beispiel haben wir die Matrizen _A_ und _B_. _A_ nutzt _double_ als Elementtyp, während _B_ eine Instantiierung von _Matrix_, bei der _float_ für _T_ eingesetzt wird. ---- CODE (type=cpp) ---------------------------------------------------------- Matrix A(7, 8, StorageOrder::ColMajor); Matrix B(3, 3, StorageOrder::RowMajor); ------------------------------------------------------------------------------- Aufgaben ======== * Vervollständigen Sie die generische Fassung der _Matrix_-Klasse und fügen Sie ein kleines Testprogramm hinzu, bei der Sie die _Matrix_-Klasse mit unterschiedlichen Elementtypen instantiieren, initialisieren und anschließend ausgeben. * Gibt es Typen, die nicht als Parameter für die Template-Klasse _Matrix_ eingesetzt werden können? :navigate: up -> doc:index next -> doc:session04/page02