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
};Перечисления в виде классов хорошо вписываются в объектно-ориентированный дизайн, делая код более читаемым и позволяя обнаруживать ошибки типов во время компиляции, а не во время выполнения.
Задание
ЛегкоДавайте создадим систему приоритетов задач, в которой классы 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_castdisplay(): выводит задачу в формате:[name] - Priority: [priority] (Value: [value]) - Status: [status]
main.cpp: считайте три входных значения:- Имя задачи (строка)
- Уровень приоритета в виде целого числа (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 полностью разделяют значения приоритета и статуса. Вы не сможете случайно сравнить 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;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
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Итоги — Обобщенный контейнерПотренируйтесь самостоятельно: Онлайн-компилятор C++