Klassentemplates
Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy — Lektion 65 von 104.
Genauso wie Funktions-Templates es Ihnen ermöglichen, typunabhängige Funktionen zu schreiben, lassen Sie Klassen-Templates typunabhängige Klassen erstellen. So funktionieren Container wie std::vector – Sie definieren die Struktur einmal, und der Compiler generiert spezifische Versionen für jeden Typ, den Sie verwenden.
template <typename T>
class Box {
T value;
public:
Box(T v) : value(v) {}
T getValue() const { return value; }
void setValue(T v) { value = v; }
};
int main() {
Box<int> intBox(42);
Box<std::string> strBox("Hello");
std::cout << intBox.getValue() << std::endl; // 42
std::cout << strBox.getValue() << std::endl; // Hello
}Im Gegensatz zu Funktions-Templates müssen Sie den Typ explizit angeben, wenn Sie Objekte aus Klassen-Templates unter Verwendung von spitzen Klammern erstellen. Der Compiler generiert dann eine vollständige Klassendefinition für diesen spezifischen Typ.
Klassentemplates können mehrere Typparameter und sogar Nicht-Typparameter wie Ganzzahlen haben:
template <typename T, int Size>
class FixedArray {
T data[Size];
public:
T& operator[](int index) { return data[index]; }
int size() const { return Size; }
};
FixedArray<double, 5> arr; // Array von 5 Doubles
arr[0] = 3.14;Wenn Sie Elementfunktionen außerhalb der Klasse definieren, müssen Sie die Template-Deklaration wiederholen:
template <typename T>
class Container {
T* data;
public:
Container();
~Container();
};
template <typename T>
Container<T>::Container() : data(nullptr) {}
template <typename T>
Container<T>::~Container() { delete data; }Klassentemplates bilden die Grundlage der generischen Programmierung in C++ und ermöglichen es Ihnen, wiederverwendbare Datenstrukturen zu schreiben, die mit jedem Typ funktionieren.
Aufgabe
EinfachLassen Sie uns ein generisches Speichersystem unter Verwendung von Klassentemplates erstellen, um einen flexiblen Container zu bauen, der jeden Datentyp aufnehmen kann. Sie werden Ihre Template-Klasse in einer Header-Datei organisieren und ihre Vielseitigkeit mit verschiedenen Typen in Ihrem Hauptprogramm demonstrieren.
Sie werden zwei Dateien erstellen:
Storage.h: Definieren Sie ein Klassentemplate namensStorage, das als einfacher Container für einen einzelnen Wert mit einigen nützlichen Operationen fungiert:Ihr
Storage-Template sollte Folgendes haben:- Ein privates Mitglied, um den gespeicherten Wert zu halten
- Einen Konstruktor, der einen Initialwert entgegennimmt
- Eine
getValue()-Methode, die den gespeicherten Wert zurückgibt - Eine
setValue()-Methode, die den gespeicherten Wert aktualisiert - Eine
isEmpty()-Methode, dietruezurückgibt, wenn der Wert dem standardmäßig konstruierten Wert vom Typ T entspricht, andernfallsfalse
Erstellen Sie außerdem ein Klassentemplate namens
Pairmit zwei Typparametern, das zwei zusammengehörige Werte speichert:- Private Mitglieder für den ersten und zweiten Wert (von potenziell unterschiedlichen Typen)
- Einen Konstruktor, der beide Werte initialisiert
- Methoden
getFirst()undgetSecond(), um die jeweiligen Werte abzurufen - Eine
display()-Methode, die Folgendes ausgibt:(<first>, <second>)
main.cpp: Lesen Sie vier Eingaben ein (jede in einer separaten Zeile):- Einen Integer-Wert
- Einen neuen Integer-Wert
- Einen String-Wert
- Einen Double-Wert
Demonstrieren Sie Ihre Templates durch:
- Erstellen eines
Storage<int>mit dem ersten Integer und Ausgabe von:Int storage: <value> - Aktualisieren desselben mit dem zweiten Integer unter Verwendung von
setValue(), dann Ausgabe von:Updated: <value> - Erstellen eines
Storage<std::string>mit der String-Eingabe und Ausgabe von:String storage: <value> - Prüfen, ob der String-Speicher leer ist, und Ausgabe von:
Is empty: <true/false>(geben Sietrueoderfalseaus) - Erstellen eines
Pair<std::string, double>mit den String- und Double-Werten, dann Aufruf vondisplay() - Erstellen eines
Pair<int, int>mit beiden Integer-Eingaben, dann Aufruf vondisplay()
Zum Beispiel bei den Eingaben 42, 100, Hello und 3.14:
Int storage: 42
Updated: 100
String storage: Hello
Is empty: false
(Hello, 3.14)
(42, 100)Beachten Sie, wie dasselbe Storage-Template nahtlos mit Integern und Strings funktioniert und wie Pair verschiedene Typen miteinander kombinieren kann. Der Compiler generiert separate Klassendefinitionen für jede Typkombination, die Sie verwenden. Denken Sie daran, die Syntax template <typename T> für Templates mit einem Typ und template <typename T, typename U> für Templates mit mehreren Typparametern zu verwenden.
Spickzettel
Klassentemplates ermöglichen es Ihnen, typunabhängige Klassen zu erstellen. Der Compiler generiert spezifische Versionen für jeden Typ, den Sie verwenden.
Grundlegendes Klassentemplate
template <typename T>
class Box {
T value;
public:
Box(T v) : value(v) {}
T getValue() const { return value; }
void setValue(T v) { value = v; }
};Beim Erstellen von Objekten aus Klassentemplates müssen Sie den Typ explizit mit spitzen Klammern angeben:
Box<int> intBox(42);
Box<std::string> strBox("Hello");Mehrere Typparameter
Klassentemplates können mehrere Typparameter haben:
template <typename T, typename U>
class Pair {
T first;
U second;
public:
Pair(T f, U s) : first(f), second(s) {}
};Nicht-Typparameter
Templates können auch Nicht-Typparameter wie Ganzzahlen akzeptieren:
template <typename T, int Size>
class FixedArray {
T data[Size];
public:
int size() const { return Size; }
};
FixedArray<double, 5> arr; // Array aus 5 DoublesDefinition von Elementfunktionen außerhalb der Klasse
Wenn Sie Elementfunktionen außerhalb der Klasse definieren, wiederholen Sie die Template-Deklaration:
template <typename T>
class Container {
T* data;
public:
Container();
~Container();
};
template <typename T>
Container<T>::Container() : data(nullptr) {}
template <typename T>
Container<T>::~Container() { delete data; }Probier es selbst
#include <iostream>
#include <string>
#include "Storage.h"
using namespace std;
int main() {
// Eingaben lesen
int intValue1;
int intValue2;
string strValue;
double doubleValue;
cin >> intValue1;
cin >> intValue2;
cin >> strValue;
cin >> doubleValue;
// TODO: Erstelle Storage<int> mit dem ersten Integer und gib aus: Int storage: <value>
// TODO: Mit dem zweiten Integer unter Verwendung von setValue() aktualisieren, gib aus: Updated: <value>
// TODO: Erstelle Storage<std::string> mit der String-Eingabe, gib aus: String storage: <value>
// TODO: Prüfe, ob der String-Speicher leer ist, gib aus: Is empty: true/false
// TODO: Erstelle Pair<std::string, double> mit String und double, rufe display() auf
// TODO: Erstelle Pair<int, int> mit beiden Integern, rufe display() auf
return 0;
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienC++ Build & KompilierungHeader-Dateien & QuelldateienNamensräume & GültigkeitsbereicheEinführung in OOP in C++Klassen vs. ObjekteDer 'this'-PointerMethoden (Elementfunktionen)Attribute (Datenelemente)Grundlagen zu Ctors & DtorsRückblick - Einfacher Taschenrechner4Klasseneigenschaften
Instanz- vs. statische MemberGetter und SetterConst-MemberfunktionenMutable-SchlüsselwortStatische Methoden und VariablenFriend-Funktionen & KlassenZusammenfassung - Bankkonto-Manager7Vererbung
Grundlagen der VererbungZugriffsstufen bei VererbungAufrufreihenfolge von Ctor & DtorÜberschreiben von MethodenVirtuelle Funktionen & VTableMehrfachvererbungVirtuelle VererbungRückblick - Mitarbeiter-Hierarchie2Speicherverwaltung
Stack vs. Heap SpeicherZeiger und ReferenzenDynamischer Speicher (new/delete)Smart Pointers in C++RAII in C++Rückblick – Dynamischer Array-Manager5Kapselung
Zugriffsspezifizierer in C++Zugriffsspezifizierer im DetailInformation HidingStruct vs. ClassVerschachtelte & innere KlassenRückblick - Studentenverwaltungssystem8Polymorphie
Kompilierzeit- vs. Laufzeit-PolymorphieFunktionsüberladungVirtuelle Funktionen – WiederholungRein virtuelle FunktionenAbstrakte KlassenInterface-Design in C++Dynamic Casting & RTTIZusammenfassung – Formen-Rechner3Konstruktoren & Destruktoren
StandardkonstruktorParametrisierter KonstruktorKopierkonstruktorVerschiebekonstruktorKonstruktor-InitialisierungslistenDelegierende KonstruktorenDestruktoren im DetailRule of Three / Five / ZeroZusammenfassung - String-Klasse6Operatorüberladung
Einführung in die OperatorüberladungArithmetische OperatorüberladungVergleichsoperatorüberladungStream-OperatorenZuweisungsoperatorüberladungÜberladen der [] und () OperatorenTypumwandlungsoperatorenRückblick - Matrix-Klasse9Templates
FunktionstemplatesKlassentemplatesTemplate-SpezialisierungVariadische TemplatesSFINAE & Type Traits GrundlagenRückblick - Generische Container