Menu
Coddy logo textTech

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

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

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

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

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

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

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

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

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

challenge icon

Задание

Легко

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

Вы организуете свой код в трех файлах:

  • TaskTypes.h: Определите ваши enum classes для системы задач.

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

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

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

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

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

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

    • setStatus(Status s) — обновляет статус задачи
    • getPriorityValue() — возвращает базовое целочисленное значение приоритета, используя static_cast
    • display() — выводит задачу в формате: [name] - Priority: [priority] (Value: [value]) - Status: [status]
  • main.cpp: Считайте три входных значения:
    1. Имя задачи (string)
    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 classes разделяют значения приоритета и статуса — вы не можете случайно сравнить Priority со Status или присвоить одно другому. Явное использование static_cast, необходимое для преобразования между целыми числами и значениями перечисления, делает намерения кода ясными и предотвращает трудноуловимые ошибки.

Шпаргалка

C++11 представил enum classes (перечисления с областью видимости), чтобы обеспечить более строгую типизацию, чем традиционные перечисления в стиле C.

Традиционные перечисления имеют проблемы с утечкой области видимости и неявными преобразованиями:

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

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

Enum classes решают эти проблемы, требуя разрешения области видимости и предотвращая неявные преобразования:

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

Для преобразования между enum classes и целыми числами используйте явное приведение типов:

// Из целого числа в enum class
Priority p = static_cast<Priority>(3);

// Из enum class в целое число
int value = static_cast<int>(p);

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

#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Проверьте себя

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

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