Menu
Coddy logo textTech

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

Часть раздела Объектно-ориентированное программирование путешествия по 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;  // Hello
}

В отличие от шаблонов функций, вы должны явно указывать тип при создании объектов из шаблонов классов, используя угловые скобки. Затем компилятор генерирует полное определение класса для этого конкретного типа.

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

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. Число с плавающей точкой (double)

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

    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> со строкой и числом с плавающей точкой, вызвать display()
    
    // TODO: Создать Pair<int, int> с обоими целыми числами, вызвать display()
    
    return 0;
}
quiz iconПроверьте себя

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

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