Einleitung: Was sind Generics in C++ und wozu dienen sie?
In der Welt der modernen Programmierung ist die Wiederverwendung von Code eines der Schlüsselprinzipien. Wenn Sie eine Summenfunktion für int und dann für double und float schreiben, möchten Sie früher oder später Doppelarbeit vermeiden. Genau hier kommen Generics (generische Programmierung) ins Spiel. In C++ wird dieses Konzept durch Vorlagen (templates) umgesetzt.
Generics ermöglichen es, Code zu schreiben, der mit jedem Datentyp funktioniert, ohne Einbußen bei der Leistung (im Gegensatz zu virtuellen Funktionen oder void*). Der Compiler generiert für jeden konkreten Typ eine separate Version der Funktion oder Klasse, was maximale Ausführungsgeschwindigkeit bietet.
In diesem Artikel werden wir untersuchen, wozu Generics in C++ dienen, wie man sie richtig deklariert und in der Praxis einsetzt, sowie typische Fehler und fortgeschrittene Techniken betrachten.
Grundlagen der Syntax: Wie man eine Vorlage deklariert und verwendet
Eine Vorlage in C++ wird mit dem Schlüsselwort template und spitzen Klammern <> deklariert, in denen der Typparameter (normalerweise typename oder class) angegeben wird.
Funktionsvorlage
Das einfachste Beispiel ist eine Funktion, die das Maximum von zwei Werten zurückgibt:
template <typename T>T max(T a, T b) { return (a > b) ? a : b;}
// Verwendung:int main() { std::cout << max(3, 7) << std::endl; // 7 std::cout << max(3.14, 2.71) << std::endl; // 3.14 std::cout << max('a', 'z') << std::endl; // z return 0;}Beachten Sie: Der Compiler leitet den Typ T basierend auf den übergebenen Argumenten selbst ab. Wenn Sie den Typ explizit angeben möchten, können Sie max<double>(3.14, 2.71) schreiben.
Klassenvorlage
Vorlagen funktionieren auch mit Klassen. Ein klassisches Beispiel ist ein einfacher Container „Paar“:
template <typename T1, typename T2>class Pair {private: T1 first; T2 second;public: Pair(T1 f, T2 s) : first(f), second(s) {} T1 getFirst() const { return first; } T2 getSecond() const { return second; }};
// Verwendung:Pair<int, std::string> student(1, "Alice");std::cout << student.getFirst() << " " << student.getSecond();Hier sind T1 und T2 Vorlagenparameter, die beliebige Typen sein können.
Praktische Beispiele: Von einfachen Containern bis zu Algorithmen
Betrachten wir einige reale Szenarien, in denen Generics unverzichtbar sind.
Universelle Sortierung
Schreiben wir eine Vorlagenfunktion zum Sortieren eines Arrays beliebigen Typs, der den Operator < unterstützt:
template <typename T>void bubbleSort(T arr[], int n) { for (int i = 0; i < n - 1; ++i) for (int j = 0; j < n - i - 1; ++j) if (arr[j] > arr[j + 1]) std::swap(arr[j], arr[j + 1]);}
// Beispiel:int intArr[] = {5, 2, 8, 1};bubbleSort(intArr, 4); // Funktioniert mit int
double dblArr[] = {3.14, 2.71, 1.41};bubbleSort(dblArr, 3); // Funktioniert mit doubleStack mit beliebigem Datentyp
Implementieren wir einen einfachen Stack basierend auf einer Vorlage:
template <typename T>class Stack {private: std::vector<T> elements;public: void push(const T& value) { elements.push_back(value); } void pop() { if (!elements.empty()) elements.pop_back(); } T top() const { return elements.back(); } bool empty() const { return elements.empty(); }};
// Verwendung:Stack<int> intStack;intStack.push(10);intStack.push(20);std::cout << intStack.top(); // 20Algorithmus „Suchen und Ersetzen“
Eine Vorlagenfunktion, die alle Vorkommen eines Wertes durch einen anderen in einem Container ersetzt:
templa