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-классы хорошо интегрируются с принципами ООП, делая код более читаемым и позволяя обнаруживать ошибки типов на этапе компиляции, а не во время выполнения.
Задание
ЛегкоДавайте создадим систему приоритетов задач, которая использует перечисления (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_castdisplay()— выводит задачу в формате:[name] - Priority: [priority] (Value: [value]) - Status: [status]
main.cpp: Считайте три входных значения:- Имя задачи (string)
- Уровень приоритета как целое число (1=Low, 2=Medium, 3=High, 4=Critical)
- Статус как целое число (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) { } // Компилируется — сравнение перечисления с intEnum 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;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыСборка и компиляция C++Заголовочные файлы и файлы исходного кодаПространства имен и область видимостиВведение в ООП на C++Классы и объектыУказатель 'this'Методы (функции-члены)Атрибуты (члены данных)Основы конструкторов и деструкторовИтоги — Простой калькулятор4Свойства классов
Члены экземпляра против статическихГеттеры и сеттерыКонстантные функции-членыКлючевое слово mutableСтатические методы и переменныеДружественные функции и классыИтоги — Менеджер банковских счетов7Наследование
Основы наследованияУровни доступа при наследованииПорядок вызова конструкторов и деструкторовПереопределение методовВиртуальные функции и VTableМножественное наследованиеВиртуальное наследованиеИтоги — Иерархия сотрудников10Обзор STL
Обзор и философия STLКонтейнеры STLИтераторыАлгоритмы STLФункторы и лямбда-выраженияИтоги: частота слов13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonFactory и Abstract FactoryПаттерн BuilderПаттерн ObserverПаттерн Strategy2Управление памятью
Стек и кучаУказатели и ссылкиДинамическая память (new/delete)Умные указатели в C++RAII в C++Итоги — Менеджер динамического массива5Инкапсуляция
Спецификаторы доступа в C++Спецификаторы доступа: подробный разборСокрытие данныхStruct против ClassВложенные и внутренние классыИтоги — Система учета студентов8Полиморфизм
Полиморфизм компиляции и времени выполненияПерегрузка функцийВиртуальные функции: повторениеЧистые виртуальные функцииАбстрактные классыПроектирование интерфейсов на C++Dynamic Casting и RTTIИтоги: Калькулятор фигур11Продвинутые концепции ООП
Композиция против наследованияМиксины через CRTPИдиома PimplСтирание типовEnum Classes и строгая типизацияОбработка исключений в ООППользовательские иерархии исключений14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикRAII как паттерн3Конструкторы и деструкторы
Конструктор по умолчаниюПараметризованный конструкторКонструктор копированияКонструктор перемещенияСписки инициализации конструктораДелегирующие конструкторыГлубокое погружение в деструкторыПравило трех / пяти / нуляПовторение — класс String6Перегрузка операторов
Введение в перегрузку операторовПерегрузка арифметических операторовПерегрузка операторов сравненияОператоры потоковПерегрузка оператора присваиванияПерегрузка операторов [] и ()Операторы преобразования типовИтоги — класс Matrix9Шаблоны
Шаблоны функцийШаблоны классовСпециализация шаблоновВариативные шаблоныОсновы SFINAE и Type TraitsИтоги — Обобщенный контейнер