Menu
CoddyTech

Шаблоны классов

Часть раздела Объектно-ориентированное программирование путешествия по C++ на Coddy. Урок 65 из 104.

Подобно тому как шаблоны функций позволяют писать функции, не зависящие от типа, шаблоны классов позволяют создавать классы, не зависящие от типа. Именно так работают контейнеры вроде std::vector — вы определяете структуру один раз, а компилятор генерирует конкретные версии для каждого используемого вами типа.

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;  // Привет
}

Обычно при создании объектов из шаблонов классов тип указывают в угловых скобках. Начиная с C++17 компилятор также может вывести его из аргументов конструктора (Box b(42); создаёт Box<int>), но если нет аргументов, по которым можно вывести тип, его необходимо указать. Затем компилятор генерирует полное определение класса для этого конкретного типа.

Шаблоны классов могут иметь несколько параметров типа и даже нетиповые параметры, например целые числа:

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;  // Массив из 5 чисел типа double
arr[0] = 3.14;

При определении функций-членов вне класса необходимо повторить объявление шаблона:

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; }

Шаблоны классов составляют основу обобщённого программирования в C++, позволяя писать повторно используемые структуры данных, которые работают с любым типом.

challenge icon

Задание

Легко

Давайте создадим универсальную систему хранения с помощью шаблонов классов, чтобы получить гибкий контейнер, способный хранить данные любого типа. Вы разместите шаблон класса в заголовочном файле и продемонстрируете его универсальность, используя данные разных типов в главной программе.

Вам предстоит создать два файла:

  • Storage.h: Определите шаблон класса под названием Storage, который служит простым контейнером для одного значения и поддерживает несколько полезных операций:

    Ваш шаблон Storage должен содержать:

    • Закрытый член для хранения значения
    • Конструктор, принимающий начальное значение
    • Метод getValue(), возвращающий хранимое значение
    • Метод setValue(), обновляющий хранимое значение
    • Метод isEmpty(), возвращающий true, если значение равно значению типа T, созданному по умолчанию, и false в противном случае

    Также создайте шаблон класса под названием Pair с двумя параметрами типа, который хранит два связанных значения:

    • Закрытые члены для первого и второго значений (возможно, разных типов)
    • Конструктор, инициализирующий оба значения
    • Методы getFirst() и getSecond() для получения каждого из значений
    • Метод display(), который выводит: (<first>, <second>)
  • main.cpp: Считайте четыре значения (каждое с отдельной строки):
    1. Целое число
    2. Новое целое число
    3. Строку
    4. Число с плавающей точкой двойной точности

    Продемонстрируйте работу шаблонов следующим образом:

    1. Создайте Storage<int> с первым целым числом и выведите: Int storage: <value>
    2. Обновите его вторым целым числом с помощью setValue(), затем выведите: Updated: <value>
    3. Создайте Storage<std::string> со введённой строкой и выведите: String storage: <value>
    4. Проверьте, пусто ли хранилище строки, и выведите: Is empty: <true/false> (выведите true или false)
    5. Создайте Pair<std::string, double> со строкой и числом двойной точности, затем вызовите display()
    6. Создайте Pair<int, int> с обоими целыми числами, затем вызовите display()

Например, для входных данных 42, 100, Hello и 3.14:

Int storage: 42
Updated: 100
String storage: Hello
Is empty: false
(Hello, 3.14)
(42, 100)

Обратите внимание: один и тот же шаблон Storage без проблем работает и с целыми числами, и со строками, а Pair позволяет объединять значения разных типов. Компилятор создает отдельные определения классов для каждой используемой вами комбинации типов. Не забудьте использовать синтаксис template <typename T> для шаблонов с одним типом и template <typename T, typename U> для шаблонов с несколькими параметрами типа.

Попробуйте сами

#include <iostream>
#include <string>
#include "Storage.h"

using namespace std;

int main() {
    // Считать входные данные
    int intValue1;
    int intValue2;
    string strValue;
    double doubleValue;
    
    cin >> intValue1;
    cin >> intValue2;
    cin >> strValue;
    cin >> doubleValue;
    
    // TODO: Создать Storage<int> с первым целым числом и вывести: Int storage: <value>
    
    // TODO: Обновить значение вторым целым числом с помощью setValue(), вывести: Updated: <value>
    
    // TODO: Создать Storage<std::string> с входной строкой и вывести: String storage: <value>
    
    // TODO: Проверить, пусто ли хранилище строки, вывести: Is empty: true/false
    
    // TODO: Создать Pair<std::string, double> со строкой и числом типа double, вызвать display()
    
    // TODO: Создать Pair<int, int> с обоими целыми числами, вызвать display()
    
    return 0;
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор C++