Menu
CoddyTech

Enum Classes и строгая типизация

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

В C++11 были введены перечисления с областью видимости (также называемые перечислениями с заданной областью видимости), чтобы устранить недостатки традиционных перечислений в стиле C. Они обеспечивают более строгую типобезопасность и предотвращают случайное неправильное использование значений перечисления.

У традиционных перечислений есть проблемы: их значения попадают в окружающую область видимости и неявно преобразуются в целые числа, что приводит к трудноуловимым ошибкам:

// Традиционный enum — проблемный вариант
enum Color { Red, Green, Blue };
enum TrafficLight { Red, Yellow, Green };  // Ошибка! Red и Green уже определены

int x = Red;  // Неявно преобразуется в int (0)
if (Red == 0) { }  // Компилируется — сравнение enum с int

Перечисления классов решают эти проблемы, задавая область видимости значений и предотвращая неявные преобразования:

enum class Color { Red, Green, Blue };
enum class TrafficLight { Red, Yellow, Green };  // OK — конфликта нет

Color c = Color::Red;      // Нужно использовать оператор разрешения области видимости
// int x = c;              // Ошибка! Неявное преобразование невозможно
int x = static_cast<int>(c);  // Требуется явное преобразование

// if (c == 0) { }         // Ошибка! Нельзя сравнивать с int
if (c == Color::Red) { }   // OK — сравниваются значения одного типа

Вы также можете указать базовый тип для управления памятью:

enum class Status : uint8_t {
    Inactive = 0,
    Active = 1,
    Pending = 2
};

Перечисления в виде классов хорошо вписываются в объектно-ориентированный дизайн, делая код более читаемым и позволяя обнаруживать ошибки типов во время компиляции, а не во время выполнения.

challenge icon

Задание

Легко

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

Вы распределите код по трём файлам:

  • TaskTypes.h: определите классы enum для системы задач.

    Создайте класс enum Priority со значениями: Low, Medium, High и Critical. Укажите int в качестве базового типа и задайте значения 1, 2, 3 и 4 соответственно.

    Создайте класс enum Status со значениями: Pending, InProgress и Completed.

    Добавьте две вспомогательные функции:

    • priorityToString(Priority p): возвращает приоритет в виде строки ("Low", "Medium", "High" или "Critical")
    • statusToString(Status s): возвращает статус в виде строки ("Pending", "InProgress" или "Completed")
  • Task.h: определите класс Task, использующий ваши классы enum.

    Класс Task должен хранить имя (строку), значение Priority и значение Status. Конструктор должен принимать имя и приоритет, а значение статуса по умолчанию должно быть Status::Pending.

    Реализуйте следующие методы:

    • setStatus(Status s): обновляет статус задачи
    • getPriorityValue(): возвращает целочисленное значение, лежащее в основе приоритета, используя static_cast
    • display(): выводит задачу в формате: [name] - Priority: [priority] (Value: [value]) - Status: [status]
  • main.cpp: считайте три входных значения:
    1. Имя задачи (строка)
    2. Уровень приоритета в виде целого числа (1=Low, 2=Medium, 3=High, 4=Critical)
    3. Статус в виде целого числа (0=Pending, 1=InProgress, 2=Completed)

    Создайте объект Task с указанными именем и приоритетом (преобразуйте целое число в соответствующее значение перечисления Priority с помощью static_cast). Затем задайте статус на основе третьего входного значения. Наконец, вызовите display(), чтобы показать сведения о задаче.

Например, при входных значениях Fix bug, 3 и 1:

Fix bug - Priority: High (Value: 3) - Status: InProgress

При входных значениях Write docs, 1 и 2:

Write docs - Priority: Low (Value: 1) - Status: Completed

При входных значениях Deploy app, 4 и 0:

Deploy app - Priority: Critical (Value: 4) - Status: Pending

Обратите внимание, что классы enum полностью разделяют значения приоритета и статуса. Вы не сможете случайно сравнить Priority со значением Status или присвоить одно другому. Явное преобразование static_cast, необходимое для преобразования целых чисел в значения перечислений и обратно, проясняет замысел кода и предотвращает трудноуловимые ошибки.

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

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

using namespace std;

int main() {
    // Считайте входные данные
    string taskName;
    int priorityInt;
    int statusInt;
    
    getline(cin, taskName);
    cin >> priorityInt;
    cin >> statusInt;
    
    // TODO: Создайте Task с заданным именем и приоритетом
    // Используйте static_cast, чтобы преобразовать priorityInt в перечисление Priority
    
    // TODO: Установите статус задачи на основе statusInt
    // Используйте static_cast, чтобы преобразовать statusInt в перечисление Status
    
    // TODO: Вызовите display(), чтобы показать сведения о задаче
    
    return 0;
}
quiz iconПроверьте себя

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

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

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