enum (скорочення від "enumeration") — це тип даних, визначений користувачем у C++, який надає імена набору константних цілих значень. Це робить ваш код більш зрозумілим і менш схильним до помилок.
Це як група псевдонімів, які ви даєте числам для легшого розуміння. Замість того, щоб пам'ятати числа як 0, 1, 2, ви присвоюєте їм імена, наприклад, SUNDAY, MONDAY тощо.
Наприклад, розклад уроків. Замість того, щоб називати періоди цифрами, ви присвоюєте їм імена предметів:
Наприклад, у команді з крикету гравці мають номери на футболках.
Замість того, щоб запам'ятовувати числа, ви запам'ятовуєте імена гравців:
enum Periods { MATHS, SCIENCE, ENGLISH };
// За замовчуванням, якщо не вказати значення, вони починаються з нуля
// MATHS = 0 (1-й урок)
// SCIENCE = 1 (2-й урок)
// ENGLISH = 2 (3-й урок)
enum Players { SACHIN = 10, DHONI = 7, VIRAT = 18 };
Технічно, enum дозволяє вам присвоювати зрозумілі для людини імена набору цілих значень (чисел).
Ці імена роблять ваш код більш зрозумілим і легким для підтримки.
#include
using namespace std;
// Оголошення enum для днів тижня
enum Day { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday };
int main() {
// Оголошення змінної типу Day
Day today = Wednesday;
// Виведення значення
cout << "Числове значення Wednesday: " << today << endl;
return 0;
}
// ВИХІД
Числове значення Wednesday: 2
// За замовчуванням, Monday = 0, Tuesday = 1, і так далі.
Призначення користувацьких значень для enum
Ми можемо призначити власні значення для констант enum:
enum Level { Easy = 1, Medium = 3, Hard = 5 };
int main() {
cout << "Значення Medium: " << Medium << endl;
return 0;
}
// ВИХІД
Значення Medium: 3
Навіщо використовувати enum?
Основні переваги:
- Покращує читабельність: Замість незрозумілих чисел ви використовуєте осмислені імена. Наприклад:
Mode::Delicateзамість0.
2.
Зменшує кількість помилок: Запобігає випадковому використанню незв'язаних констант. - Легше обслуговування: Зміна значення enum не потребує пошуку по всьому коду.
Деякі реальні приклади використання:
Enum часто застосовуються в таких сценаріях:
- Стан світлофора (
Red,Yellow,Green). - Коди помилок (
Success,NotFound,PermissionDenied). - Рівні складності в іграх (
Easy,Medium,Hard).
Перевірте своє розуміння (відповідь надається внизу сторінки)
Запитання 1: Як ви оголосите enum для Seasons зі значеннями Spring, Summer, Autumn та Winter?
Запитання 2: Яке значення буде присвоєно Autumn за замовчуванням у цьому enum?
Запитання 3: Чи можна присвоїти значення 0 кільком константам в одному enum? Чому чи чому ні?
Обмеження традиційних enum
1.
Забруднення простору імен без області:
Усі константи в enum знаходяться в глобальній області, що може призвести до конфліктів імен.
enum Color { Red, Green, Blue };
enum TrafficLight { Red, Yellow, Green }; // Помилка:
2. Проблеми з типобезпекою
Традиційні enum не є типобезпечними. Вони неявно конвертуються в цілі числа.
enum Day { Monday, Tuesday };
int x = Monday; // Дозволено (але може призвести до помилок у великих програмах)
enum class
Щоб вирішити ці проблеми, C++ ввів enum class.
Основні особливості enum class
- Константи з областю видимості:
Константи вenum classмають область видимості в межах самого enum.
enum class Day { Monday, Tuesday, Wednesday };
enum class Month { January, February };
Day today = Day::Monday; // Область видимості: використовуйте `Day::Monday`
Month thisMonth = Month::January; // Область видимості: використовуйте `Month::January`
2. Типобезпека:
Константи enum не можуть неявно перетворюватися на цілі числа.
Приклад
enum class Day { Monday, Tuesday };
int x = Day::Monday; // Помилка: неможливо неявно перетворити на int
Синтаксис enum class
enum class TrafficLight { Red = 1, Yellow = 3, Green = 5 };
int main() {
TrafficLight signal = TrafficLight::Green;
// Безпечно отримуємо значення за допомогою явного перетворення типів
int signalValue = static_cast<int>(signal);
cout << "Значення сигналу: " << signalValue << endl; // Виведення: 5
return 0;
}
Відмінності між традиційним enum та enum class
Просунуті застосування
1.
Enum з користувацькими функціями
Ми можемо поєднати enum з допоміжними функціями для більш складних операцій:
#include
using namespace std;
enum class TrafficLight { Red, Yellow, Green };
string getSignalMeaning(TrafficLight signal) {
switch (signal) {
case TrafficLight::Red: return "Зупинка";
case TrafficLight::Yellow: return "Увага";
case TrafficLight::Green: return "Їдь";
}
return "Невірно";
}
int main() {
TrafficLight signal = TrafficLight::Yellow;
cout << "Значення сигналу: " << getSignalMeaning(signal) << endl;
return 0;
}
2.
Enum як члени класу
Ми можемо використовувати enums як частину класу для визначення специфічних поведінок.
class Traffic {
public:
enum class Light { Red, Yellow, Green };
void display(Light light) {
switch (light) {
case Light::Red: cout << "Зупинитись!" << endl; break;
case Light::Yellow: cout << "Приготуйтеся." << endl; break;
case Light::Green: cout << "Їдьте!" << endl; break;
}
}
};
int main() {
Traffic traffic;
traffic.display(Traffic::Light::Green);
return 0;
}
Перевірте своє розуміння (відповіді надані внизу сторінки)
Питання 4. Чому enum class безпечніший за традиційні enum?
Питання 5. Чи можете ви написати enum class для розмірів кави (Small, Medium, Large) з значеннями 100, 200 і 300?
Питання 6.
Що буде виведено за допомогою наступного коду?
enum class Fruit { Apple = 1, Banana = 2 };
int fruitValue = static_cast<int>(Fruit::Banana);
cout << fruitValue;
НЕОБОВ'ЯЗКОВО
Реальні випадки використання Enums
- Випадок використання 1: Машини станів
Enums часто використовуються для представлення станів у системі, таких як життєвий цикл процесу або режими роботи програми.
Приклад: Машина станів для світлофора
#include
using namespace std;
enum class TrafficLight { Red, Yellow, Green };
void displayState(TrafficLight light) {
switch (light) {
case TrafficLight::Red:
cout << "Стоп!" << endl;
break;
case TrafficLight::Yellow:
cout << "Приготуйтеся!" << endl;
break;
case TrafficLight::Green:
cout << "Поїхали!" << endl;
break;
}
}
int main() {
TrafficLight currentState = TrafficLight::Red;
displayState(currentState);
currentState = TrafficLight::Green;
displayState(currentState);
return 0;
}
Ключові моменти:
- Константи enum представляють стани.
- Оператор
switchпрацює з поточним станом для визначення поведінки.
2. Випадок використання 2: Налаштування конфігурації
Enums можна використовувати для визначення різних налаштувань або режимів у програмному забезпеченні.
Приклад: Режими додатка
enum class AppMode { Light, Dark, Auto };
void setAppMode(AppMode mode) {
if (mode == AppMode::Light)
cout << "Активовано світлий режим." << endl;
else if (mode == AppMode::Dark)
cout << "Активовано темний режим." << endl;
else
cout << "Активовано автоматичний режим." << endl;
}
int main() {
AppMode currentMode = AppMode::Dark;
setAppMode(currentMode);
return 0;
}
3. Випадок використання 3: Дозволи або прапорці
Enums з бітовими прапорцями корисні для дозволів або перемикачів функцій.
Приклад: Дозволи файлів
enum Permission { Read = 1, Write = 2, Execute = 4 };
void checkPermission(int permissions, Permission perm) {
if (permissions & perm)
cout << "Дозвіл надано!" << endl;
else
cout << "Дозвіл відхилено!" << endl;
}
int main() {
int myPermissions = Read | Write; // Поєднання дозволів Read та Write
checkPermission(myPermissions, Read); // Вивід: Дозвіл надано!
checkPermission(myPermissions, Execute); // Вивід: Дозвіл відхилено!
return 0;
}
Просунуті техніки роботи з Enums
Техніка 1: Enums та функції
Enums часто використовуються разом з функціями для виконання спеціалізованих операцій.
Приклад: Система оцінювання
#include
using namespace std;
enum class Grade { A, B, C, D, F };
string gradeToString(Grade g) {
switch (g) {
case Grade::A: return "Відмінно";
case Grade::B: return "Добре";
case Grade::C: return "Середньо";
case Grade::D: return "Нижче середнього";
case Grade::F: return "Незадовільно";
}
return "Невірна оцінка";
}
int main() {
Grade studentGrade = Grade::B;
cout << "Оцінка студента: " << gradeToString(studentGrade) << endl;
return 0;
}
Техніка 2: Enum у колекціях
Enums можна зберігати та обробляти в колекціях, таких як масиви або вектори.
Приклад: Enum у векторі
#include
#include
using namespace std;
enum class Fruit { Apple, Banana, Mango };
int main() {
vector<Fruit> fruits = { Fruit::Apple, Fruit::Mango, Fruit::Banana };
for (auto f : fruits) {
switch (f) {
case Fruit::Apple:
cout << "Яблуко" << endl;
break;
case Fruit::Banana:
cout << "Банан" << endl;
break;
case Fruit::Mango:
cout << "Манго" << endl;
break;
}
}
return 0;
}
Техніка 3: Enum з шаблонами
Enums можна поєднувати з шаблонами для надання універсальної та багаторазової функціональності.
Приклад: Шаблон для операцій з Enum
#include
#include
using namespace std;
enum class Color { Red, Green, Blue };
template <typename T>
typename enable_if<is_enum<T>::value, int>::type toInt(T value) {
return static_cast<int>(value);
}
int main() {
Color color = Color::Green;
cout << "Значення кольору: " << toInt(color) << endl; // Вивід: 1
return 0;
}
Перевірте своє розуміння
Питання 7. Машина станів:
a. Створіть enum class DoorState зі значеннями Open (Відкрито), Closed (Закрито) та Locked (Заблоковано).
b. Напишіть функцію для виведення повідомлення для кожного стану.
Питання 8. Налаштування конфігурації:
a. Створіть enum class VolumeLevel зі значеннями Mute = 0 (Без звуку), Low = 1 (Низький), Medium = 2 (Середній), High = 3 (Високий).
b. Напишіть функцію, яка налаштовує гучність на основі цих рівнів.
Питання 9. Дозволи:
a. Створіть enum Access зі значеннями Read = 1 (Читання), Write = 2 (Запис), Execute = 4 (Виконання).
b.
Напишіть функцію для перевірки, чи дозволено певне право доступу для файлу.
ВІДПОВІДІ
- Щоб оголосити
enumдля Сезонів з значеннямиSpring(Весна),Summer(Літо),Autumn(Осінь) таWinter(Зима):
enum Seasons { Spring, Summer, Autumn, Winter };
-
За замовчуванням значення в
enumпризначаються послідовно, починаючи з0. Якщо не визначено явно, то відповідь на це питання буде 2. -
Так, ми можемо призначати подібні значення, але це не є найкращою практикою. Наприклад, можна написати:
enum Status { SUCCESS = 0, FAILURE = 1, IN_PROGRESS = 0 };
- enumclass_ є безпечнішим за enum, оскільки надає строгу перевірку типів та уникає конфліктів імен. Ось чому:
i) Немає неявного перетворення в цілі числа
У традиційних enums значення можна використовувати як цілі числа, що може призвести до помилок:
enum Color { Red, Green, Blue };
int x = Red; // Дозволено (Red = 0 за замовчуванням)
У enum class це не дозволено.
Hey! How’s it going?
З використанням enum class компілятор запобігає таким помилкам:
enum class Fruits { Apple, Banana };
enum class Colors { Red, Green };
Fruits f = Fruits::Apple;
if (f == Colors::Green) { // Помилка: неможливо порівняти різні класи переліку
// ...
}
5. Клас переліку для розмірів кави
#include
enum class CoffeeSize {
Small = 100, // Маленька кава - 100 мл
Medium = 200, // Середня кава - 200 мл
Large = 300 // Велика кава - 300 мл
};
int main() {
CoffeeSize size = CoffeeSize::Medium;
std::cout << "Розмір кави в мл: " << static_cast(size) << std::endl;
return 0;
}
// ВИХІД
Розмір кави в мл: 200
6. Вихід буде 2, оскільки значення задані послідовно:
Monday = 1Tuesday = 2Wednesday = 3
У коді:
todayвстановлено наDays::Tuesday, яке має значення 2.static_cast(today)перетворюєDays::Tuesdayна його ціле значення 2.
7.
a.
Створення класу переліку DoorState
Визначте enum class для станів дверей:
enum class DoorState {
Open,
Closed,
Locked
};
b. Напишіть функцію для виведення повідомлення для кожного стану
#include
#include
enum class DoorState {
Open,
Closed,
Locked
};
void displayDoorState(DoorState state) {
switch (state) {
case DoorState::Open:
std::cout << "Двері відкриті." << std::endl;
break;
case DoorState::Closed:
std::cout << "Двері закриті." << std::endl;
break;
case DoorState::Locked:
std::cout << "Двері заблоковані." << std::endl;
break;
}
}
int main() {
DoorState state = DoorState::Locked;
displayDoorState(state);
return 0;
}
8. Налаштування конфігурації
a. Визначте enum class для рівнів гучності:
enum class VolumeLevel {
Mute = 0,
Low = 1,
Medium = 2,
High = 3
};
b.
функція, яка регулює гучність залежно від рівня:
#include
enum class VolumeLevel {
Mute = 0,
Low = 1,
Medium = 2,
High = 3
};
void adjustVolume(VolumeLevel level) {
switch (level) {
case VolumeLevel::Mute:
std::cout << "Гучність встановлено на Мут." << std::endl;
break;
case VolumeLevel::Low:
std::cout << "Гучність встановлено на Низьку." << std::endl;
break;
case VolumeLevel::Medium:
std::cout << "Гучність встановлено на Середню." << std::endl;
break;
case VolumeLevel::High:
std::cout << "Гучність встановлено на Високу." << std::endl;
break;
}
}
int main() {
adjustVolume(VolumeLevel::Medium);
return 0;
}
9. Права доступу
a. enum class для прав доступу до файлів:
enum class Access {
Read = 1, // Бінарне: 001
Write = 2, // Бінарне: 010
Execute = 4 // Бінарне: 100
};
b.
функція для перевірки, чи дозволено певне право доступу
#include
enum class Access {
Read = 1, // Бінарне: 001
Write = 2, // Бінарне: 010
Execute = 4 // Бінарне: 100
};
bool hasPermission(int permissions, Access check) {
return (permissions & static_cast<int>(check)) != 0;
}
int main() {
int filePermissions = 3; // Бінарне: 011 (Read + Write)
if (hasPermission(filePermissions, Access::Read)) {
std::cout << "Дозвіл на читання надано." << std::endl;
}
if (hasPermission(filePermissions, Access::Write)) {
std::cout << "Дозвіл на запис надано." << std::endl;
}
if (hasPermission(filePermissions, Access::Execute)) {
std::cout << "Дозвіл на виконання надано." << std::endl;
}
return 0;
}
// ВИХІД
Дозвіл на читання надано.
Дозвіл на запис надано.
Перекладено з: [Enums in C++: Real-World Use Cases and Advanced Techniques](https://medium.com/@dubeyshubham108/enums-in-c-real-world-use-cases-and-advanced-techniques-3c24a1e65895?source=rss------c-5)