Klassen-Templates
Wir wollen ja nicht immer alle Arbeit selbst machen, also bietet uns C++ eine Möglichkeit, den Compiler in bestimmten Fällen die Hauptarbeit machen zu lassen. Klassen-Templates sind genau für diese Fälle da: wir haben ein Problem, welches wir mit Hilfe einer Klasse lösen wollen, was aber für unterschiedliche Typen immer wieder gleich gelöst werden muss. Statt jedesmal eine neue Klasse zu schreiben, geben wir dem Compiler einmal eine Blaupause, aus der er sich dann bei Bedarf die passenden Klassen selbst erzeugen kann.
Video
Code
#include <string>
#include <iostream>
template <typename T> class Optional
{
T mValue;
bool mHasValue;
public:
Optional() : mHasValue(false) { }
Optional(T const &v) : mValue(v), mHasValue(true) { }
void set(T const &v)
{
mValue = v;
mHasValue = true;
}
void clear()
{
mHasValue = false;
}
T const &get() const
{
if (!mHasValue) {
std::cerr << "Vorsicht! Leere Variable ausgelesen!\n";
}
return mValue;
}
bool hasValue() const
{
return mHasValue;
}
};
int main()
{
Optional<std::string> o;
std::cout << "Hat o im Moment einen Wert? " << o.hasValue() << "\n";
o.set("Test");
std::cout << "Hat o im Moment einen Wert? " << o.hasValue() << "\n";
std::cout << "o = " << o.get() << "\n";
std::cout << "i = " << i.get() << "\n";
o.clear();
std::cout << "Hat o im Moment einen Wert? " << o.hasValue() << "\n";
}
Erklärung
Klassen-Templates sind für sich genommen keine eigenständigen Klassen, d.h. sie
definieren keine neuen Typen. Sie sind vielmehr eine Blaupause, nach der der
Compiler für uns neue Klassen generieren kann. Zu diesem Zweck haben sie eine
eigene Parameterliste, die aber statt Werten (wie bei einer Funktion) Typen
enthält (Zeile 4, der Teil in < .. >). Die Template-Parameter können in der
Definition des Templates anstelle von Typen verwendet werden und werden bei der
Instanziierung des Templates zu einem konkreten Typ dann durch den eingesetzten
Parameter ersetzt. Die Instanziierung des Templates ist im Beispiel oben in
Zeile 43 zu sehen. Der Name des Templates wird wie ein Typ verwendet, aber
zusätzlich kommt dahinter in spitzen Klammern noch die Liste der konkreten
Parameter für diese Instanz (im Beispiel std::string). Der Compiler wird nun
das Template hernehmen und eine neue Klasse generieren, wo jedes Vorkommen des
Templateparamters T durch std::string ersetzt wird. Diese neue generierte
Klasse wird dann zu Binärcode übersetzt (mit allen Tricks, die der Compiler so
beherrscht, wie Typprüfung, Optimierung etc.).
Wenn wir wie im Video das Template nochmal für int instanziieren, dann
generiert der Compiler eine zweite Klasse mit eben der Ersetzung durch int.
Diese beiden generierten Klassen definieren dann völlig unterschiedliche Typen
aus Sicht des Compilers und können nicht ineinander umgewandelt werden. Sobald
die Template-Ersetzung stattgefunden hat, besteht zwischen den beiden Klassen
für den Compiler nicht mehr Zusammenhang, als zwischen zwei Klassen, die wir
zufällig ähnlich programmiert haben. Das ist bspw. ein Unterschied zu den
Generics in Java, bei denen nur eine Variante der Klasse existiert und die
Template-Parameter nur für die Typprüfung des Compilers interessant sind (und
zur Laufzeit nichts mehr über diese Parameter bekannt ist). Durch diese
Eigenart, aus den Templates wirklich neue Klassen zu generieren, kann C++ noch
einige Tricks bieten, die wir uns in zukünftigen Videos noch anschauen werden.