Szablony klas
Część sekcji Programowanie obiektowe ścieżki C++ w Coddy. Lekcja 65 z 104.
Podobnie jak szablony funkcji pozwalają pisać funkcje niezależne od typu, szablony klas umożliwiają tworzenie klas niezależnych od typu. W ten sposób działają kontenery takie jak std::vector — definiujesz strukturę raz, a kompilator generuje konkretne wersje dla każdego używanego typu.
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
}W przeciwieństwie do szablonów funkcji, podczas tworzenia obiektów z szablonów klas musisz jawnie określić typ, używając nawiasów ostrych. Kompilator generuje wtedy kompletną definicję klasy dla tego konkretnego typu.
Szablony klas mogą mieć wiele parametrów typów, a nawet parametry niebędące typami, takie jak liczby całkowite:
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; // Tablica 5 wartości typu double
arr[0] = 3.14;Definiując funkcje składowe poza klasą, musisz powtórzyć deklarację szablonu:
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; }Szablony klas stanowią podstawę programowania generycznego w C++, umożliwiając pisanie wielokrotnego użytku struktur danych, które działają z dowolnym typem.
Wyzwanie
ŁatwyStwórzmy generyczny system przechowywania danych za pomocą szablonów klas, aby utworzyć elastyczny kontener, który może przechowywać dane dowolnego typu. Umieścisz szablon klasy w pliku nagłówkowym i zademonstrujesz jego wszechstronność, używając różnych typów w głównym programie.
Utworzysz dwa pliki:
Storage.h: Zdefiniuj szablon klasy o nazwieStorage, który będzie prostym kontenerem na pojedynczą wartość z kilkoma przydatnymi operacjami:Szablon
Storagepowinien zawierać:- Prywatne pole przechowujące wartość
- Konstruktor przyjmujący wartość początkową
- Metodę
getValue(), która zwraca przechowywaną wartość - Metodę
setValue(), która aktualizuje przechowywaną wartość - Metodę
isEmpty(), która zwracatrue, jeśli wartość jest równa wartości domyślnej skonstruowanej dla typu T, a w przeciwnym raziefalse
Utwórz też szablon klasy o nazwie
Pairz dwoma parametrami typu, który przechowuje dwie powiązane wartości:- Prywatne pola przechowujące pierwszą i drugą wartość (potencjalnie różnych typów)
- Konstruktor inicjalizujący obie wartości
- Metody
getFirst()igetSecond()pobierające odpowiednio każdą wartość - Metodę
display(), która wyświetla:(<first>, <second>)
main.cpp: Wczytaj cztery dane wejściowe (każde w osobnym wierszu):- Wartość całkowitą
- Nową wartość całkowitą
- Wartość typu string
- Wartość typu double
Zademonstruj działanie szablonów, wykonując następujące czynności:
- Utwórz obiekt
Storage<int>z pierwszą liczbą całkowitą i wyświetl:Int storage: <value> - Zaktualizuj go drugą liczbą całkowitą za pomocą
setValue(), a następnie wyświetl:Updated: <value> - Utwórz obiekt
Storage<std::string>z wczytanym tekstem i wyświetl:String storage: <value> - Sprawdź, czy obiekt przechowujący tekst jest pusty, i wyświetl:
Is empty: <true/false>(wyświetltruelubfalse) - Utwórz obiekt
Pair<std::string, double>z wartościami typu string i double, a następnie wywołajdisplay() - Utwórz obiekt
Pair<int, int>z obiema wczytanymi liczbami całkowitymi, a następnie wywołajdisplay()
Na przykład dla danych wejściowych 42, 100, Hello i 3.14:
Int storage: 42
Updated: 100
String storage: Hello
Is empty: false
(Hello, 3.14)
(42, 100)Zwróć uwagę, że ten sam szablon Storage działa bezproblemowo zarówno z liczbami całkowitymi, jak i tekstem, a Pair może łączyć ze sobą różne typy. Kompilator generuje osobne definicje klas dla każdej użytej kombinacji typów. Pamiętaj, aby stosować składnię template <typename T> dla szablonów z jednym typem oraz template <typename T, typename U> dla szablonów z wieloma parametrami typu.
Spróbuj swoich sił
#include <iostream>
#include <string>
#include "Storage.h"
using namespace std;
int main() {
// Wczytaj dane wejściowe
int intValue1;
int intValue2;
string strValue;
double doubleValue;
cin >> intValue1;
cin >> intValue2;
cin >> strValue;
cin >> doubleValue;
// TODO: Utwórz Storage<int> z pierwszą liczbą całkowitą i wypisz: Int storage: <value>
// TODO: Zaktualizuj wartość drugą liczbą całkowitą za pomocą setValue(), wypisz: Updated: <value>
// TODO: Utwórz Storage<std::string> z wczytanym ciągiem znaków i wypisz: String storage: <value>
// TODO: Sprawdź, czy Storage<std::string> jest pusty, wypisz: Is empty: true/false
// TODO: Utwórz Pair<std::string, double> z ciągiem znaków i liczbą typu double, wywołaj display()
// TODO: Utwórz Pair<int, int> z obu liczb całkowitych, wywołaj display()
return 0;
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrzneBudowanie i kompilacja C++Pliki nagłówkowe i pliki źródłowePrzestrzenie nazw i zakresWprowadzenie do programowania obiektowego w C++Klasy a obiektyWskaźnik „this”Metody (funkcje składowe)Atrybuty (składowe danych)Podstawy konstruktorów i destruktorówPowtórzenie – prosty kalkulator4Właściwości klas
Elementy instancji a elementy statyczneGettery i setteryStałe funkcje składoweSłowo kluczowe mutableMetody i zmienne statyczneFunkcje i klasy zaprzyjaźnionePodsumowanie – menedżer konta bankowego7Dziedziczenie
Podstawy dziedziczeniaPoziomy dostępu w dziedziczeniuKolejność wywołań konstruktora i destruktoraNadpisywanie metodFunkcje wirtualne i VTableWielokrotne dziedziczenieDziedziczenie wirtualnePowtórzenie — hierarchia pracowników2Zarządzanie pamięcią
Pamięć stosu a stertyWskaźniki i referencjePamięć dynamiczna (new/delete)Inteligentne wskaźniki w C++RAII w C++Podsumowanie — menedżer tablicy dynamicznej5Hermetyzacja
Specyfikatory dostępu w C++Specyfikatory dostępu — szczegółowoUkrywanie informacjiStruktura a klasaKlasy zagnieżdżone i wewnętrznePodsumowanie — system ewidencji studentów8Polimorfizm
Polimorfizm czasu kompilacji i wykonaniaPrzeciążanie funkcjiFunkcje wirtualne — powtórkaCzysto wirtualne funkcjeKlasy abstrakcyjneProjektowanie interfejsów w C++Rzutowanie dynamiczne i RTTIPodsumowanie — kalkulator kształtów3Konstruktory i destruktory
Konstruktor domyślnyKonstruktor z parametramiKonstruktor kopiującyKonstruktor przenoszącyListy inicjalizacyjne konstruktoraKonstruktory delegująceDestruktor — szczegółowe omówienieZasada trzech / pięciu / zeraPodsumowanie — klasa String6Przeciążanie operatorów
Wprowadzenie do przeciążania operatorówPrzeciążanie operatorów arytmetycznychPrzeciążanie operatorów porównaniaOperatory strumieniowePrzeciążanie operatora przypisaniaPrzeciążanie operatorów [] i ()Operatory konwersji typówPodsumowanie — klasa Matrix9Szablony
Szablony funkcjiSzablony klasSpecjalizacja szablonówSzablony wariadycznePodstawy SFINAE i cech typówPodsumowanie — kontener generycznyPoćwicz samodzielnie: Kompilator C++ online