Переліки в C++: реальні сценарії використання та просунуті техніки

pic

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?

Основні переваги:

  1. Покращує читабельність: Замість незрозумілих чисел ви використовуєте осмислені імена. Наприклад: Mode::Delicate замість 0.
    2.
    Зменшує кількість помилок: Запобігає випадковому використанню незв'язаних констант.
  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

  1. Константи з областю видимості:
    Константи в 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

pic

Просунуті застосування

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. Випадок використання 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.
Напишіть функцію для перевірки, чи дозволено певне право доступу для файлу.

ВІДПОВІДІ

  1. Щоб оголосити enum для Сезонів з значеннями Spring (Весна), Summer (Літо), Autumn (Осінь) та Winter (Зима):
enum Seasons { Spring, Summer, Autumn, Winter };
  1. За замовчуванням значення в enum призначаються послідовно, починаючи з 0. Якщо не визначено явно, то відповідь на це питання буде 2.

  2. Так, ми можемо призначати подібні значення, але це не є найкращою практикою. Наприклад, можна написати:

enum Status { SUCCESS = 0, FAILURE = 1, IN_PROGRESS = 0 };
  1. 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)

Leave a Reply

Your email address will not be published. Required fields are marked *