Menu
Coddy logo textTech

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.

challenge icon

Wyzwanie

Łatwy

Stwó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 nazwie Storage, który będzie prostym kontenerem na pojedynczą wartość z kilkoma przydatnymi operacjami:

    Szablon Storage powinien 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 zwraca true, jeśli wartość jest równa wartości domyślnej skonstruowanej dla typu T, a w przeciwnym razie false

    Utwórz też szablon klasy o nazwie Pair z 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() i getSecond() 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):
    1. Wartość całkowitą
    2. Nową wartość całkowitą
    3. Wartość typu string
    4. Wartość typu double

    Zademonstruj działanie szablonów, wykonując następujące czynności:

    1. Utwórz obiekt Storage<int> z pierwszą liczbą całkowitą i wyświetl: Int storage: <value>
    2. Zaktualizuj go drugą liczbą całkowitą za pomocą setValue(), a następnie wyświetl: Updated: <value>
    3. Utwórz obiekt Storage<std::string> z wczytanym tekstem i wyświetl: String storage: <value>
    4. Sprawdź, czy obiekt przechowujący tekst jest pusty, i wyświetl: Is empty: <true/false> (wyświetl true lub false)
    5. Utwórz obiekt Pair<std::string, double> z wartościami typu string i double, a następnie wywołaj display()
    6. Utwórz obiekt Pair<int, int> z obiema wczytanymi liczbami całkowitymi, a następnie wywołaj display()

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;
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator C++ online