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 = 1
Tuesday = 2
Wednesday = 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)