Evgenii Legotckoi
Evgenii Legotckoi02 вересня 2017 р. 07:25

C++ - Підручник 008. Перерахування

Крім класів C++ підтримує перерахування. У сучасному стандарті C++ підтримуються як перерахування без видимості, які були введені в ранніх версіях C++, а також C.

enum ColorTypes {
    Green,
    Yellow,
    Red
};

Так і перерахування з областю видимості

enum class ColorTypes {
    Green,
    Yellow,
    Red
};

Відмінність перерахувань з областю видимості від перерахувань без області видимості

Відмінність перерахування з областю видимістю відрізняється від перерахунку без області видимості тим, що змінні перерахувань з областю видимості не можуть неявно перетворюватися на цілі змінні і назад. Для такого перетворення слід використовувати static_cast ().

enum class ColorTypes { Green, Yellow, Red };

ColorTypes currentType_1 = ColorTypes::Green; // Ok
ColorTypes currentType_2 = 2; // Error, преобразование невозможно
int currentType_3 = ColorTypes::Red; // Error, преобразование невозможно
int currentType_4 = Red; // Error, Red не существует в данной области видимости
int currentType_5 = static_cast<int>(ColorTypes::Green);

Подібний контроль областей видимості дозволяє оголошувати у класі перерахування, члени яких мають однакові імена. Також надає більший контроль над кодом, хоч і накладає низку обмежень.

Як задаються значення перерахувань

За замовчуванням перерахування починається з 0, і далі відбувається інкремент членів перерахування.

enum class ColorTypes {
    Green,      // 0
    Yellow,     // 1
    Red,        // 2
    Black,      // 3
    Blue        // 4
};

Проте є можливість ставити власні значення для перерахувань під час оголошення.

enum class ColorTypes {
    Green = 15,                 // 15
    Yellow,                     // 16
    Red = 24,                   // 24
    Black = ColorTypes::Red,    // 24
    Blue                        // 25
};

Використання switch case для перерахувань

Перерахування як із областю видимості, так і без області видимості підтримують оператори умов та розгалуження switch/case :

ColorTypes currentType = ColorTypes::Green;

switch (currentType)
{
    case ColorTypes::Green: std::cout << "Green"; break;
    case ColorTypes::Yellow: std::cout << "Yellow"; break;
    case ColorTypes::Black: std::cout << "Black"; break;
    case ColorTypes::Blue: std::cout << "Blue"; break;
    default: std::cout << "Unknown Type"; break;
}

Якщо міркувати про контроль виконання коду і обмеження, що накладаються при використанні перерахувань з областю видимості і без області видимості, то можна змоделювати ситуацію, коли в switch/case з якоїсь причини (друкарська помилка, копіпаста, нуб, напортачили при вирішенні конфліктів при мердже гілок) потрапляють перерахування різних типів. Тоді перерахування без області видимості будуть неявно перетворені на потрібний тип компілятором і код виконається, хоча і буде помилковим, а у випадку з перерахуваннями з областю видимості компілятор повідомить про помилку і припинить складання програми.

Тобто нижче наступний код, будучи помилковим, скомпілюється:

enum SideTypes {
    Top,
    Bottom,
    Right,
    Left
};

enum ColorTypes {
    Green = 8,
    Yellow,
    Red,
    Blue
};

int main(int argc, char *argv[])
{
    ColorTypes currentType = ColorTypes::Green;

    switch (currentType)
    {
        case SideTypes::Top: std::cout << "Top Side"; break;
        case ColorTypes::Green: std::cout << "Green"; break;
        case ColorTypes::Yellow: std::cout << "Yellow"; break;
        case ColorTypes::Red: std::cout << "Red"; break;
        case ColorTypes::Blue: std::cout << "Blue"; break;
        default: std::cout << "Unknown Type"; break;
    }

    return 0;
}

У кращому разі компілятор викине Warning.

warning: case value ‘0’ not in enumerated type ‘ColorTypes’ [-Wswitch]
         case SideTypes::Top: std::cout << "Top Side"; break;
         ^

Але буває так, що програміст "краще компілятора знає і розуміє С++", і відключає попередження.

Тоді як наступний код просто не скомпілюється:

enum class SideTypes {
    Top,
    Bottom,
    Right,
    Left
};

enum class ColorTypes {
    Green = 8,
    Yellow,
    Red,
    Blue
};

int main(int argc, char *argv[])
{
    ColorTypes currentType = ColorTypes::Green;

    switch (currentType)
    {
        case SideTypes::Top: std::cout << "Top Side"; break;
        case ColorTypes::Green: std::cout << "Green"; break;
        case ColorTypes::Yellow: std::cout << "Yellow"; break;
        case ColorTypes::Red: std::cout << "Red"; break;
        case ColorTypes::Blue: std::cout << "Blue"; break;
        default: std::cout << "Unknown Type"; break;
    }
    return 0;
}

Копілятор видасть помилку компіляції:

error: could not convert ‘Top’ from ‘SideTypes’ to ‘ColorTypes’
         case SideTypes::Top: std::cout << "Top Side"; break;
                         ^

Як задати конкретний цілісний тип для перерахування

Перерахування також можуть мати більш конкретизований тип, який повинен бути певним цілим типом:

  • непідписаний символ;
  • char;
  • int;
  • довгий int;
  • і т.д.

Це дозволяє виділити певний обсяг пам'яті під змінні, які мають значення перерахувань. Мабуть, це може бути актуальним для embedded розробки. Залежно від цільової платформи виділятиметься певний обсяг пам'яті.

enum class SideTypes : short int {
    Top,
    Bottom,
    Right,
    Left
};

Ітератор для перерахування

І насамкінець зробимо ітератор для перерахувань, за допомогою яких можна використовувати range-based цикл for .

#include <iostream>

enum class ColorTypes
{
    Blue,
    Red,
    Green,
    Purple,
    First=ColorTypes::Blue,   // участник перечисления для первого элемента
    Last=ColorTypes::Purple // участник перечисления для последнего элемента
};

ColorTypes operator++(ColorTypes& x)
{
    // std::underlying_type преобразовывает тип ColorTypes в целочисленный тип, под которым данный enum был объявлен
    return x = static_cast<ColorTypes>(std::underlying_type<ColorTypes>::type(x) + 1);
}

ColorTypes operator*(ColorTypes c)
{
    return c;
}

ColorTypes begin(ColorTypes r)
{
    return ColorTypes::First;
}

ColorTypes end(ColorTypes r)
{
    ColorTypes l=ColorTypes::Last;
    return ++l;
}

int main(int argc, char *argv[])
{
    // Используем круглые скобки для инстанцирования перечисления
    for(const auto& c : ColorTypes())
    {
        std::cout << static_cast<int>(c) << std::endl;
    }

    return 0;
}
Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Стабільний хостинг, на якому розміщується соціальна мережа EVILEG. Для проектів на Django радимо VDS хостинг.

Вам це подобається? Поділіться в соціальних мережах!

ДК
  • 26 листопада 2019 р. 09:38
  • (відредаговано)

классная статья! Большое спасибо. Хотел бы добавить для тех, кто будет использовать это в другом классе- перед операторами и методами begin(), end() нужно поставить friend.

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up
Дмитрий

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:60бали,
  • Рейтинг балів-1
Дмитрий

C++ - Тест 003. Условия и циклы

  • Результат:92бали,
  • Рейтинг балів8
d
  • dsfs
  • 26 квітня 2024 р. 04:56

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:80бали,
  • Рейтинг балів4
Останні коментарі
k
kmssr08 лютого 2024 р. 18:43
Qt Linux - Урок 001. Автозапуск програми Qt під Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий Кононенко05 лютого 2024 р. 01:50
Qt WinAPI - Урок 007. Робота з ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25 грудня 2023 р. 10:30
Boost - статичне зв&#39;язування в проекті CMake під Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJo25 грудня 2023 р. 08:38
Boost - статичне зв&#39;язування в проекті CMake під Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
Gvozdik18 грудня 2023 р. 21:01
Qt/C++ - Урок 056. Підключення бібліотеки Boost в Qt для компіляторів MinGW і MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Тепер обговоріть на форумі
G
George1307 травня 2024 р. 00:27
добавить qlineseries в функции в функции: "GPlotter::addSeries(QString title, QVector &arr)" я вызываю метод setChart(...), я в конструктор передал адрес на QChartView элемент
BlinCT
BlinCT05 травня 2024 р. 05:46
Написать свой GraphsView Всем привет. В Qt есть давольно старый обьект дял работы с графиками ChartsView и есть в 6.7 новый но очень сырой и со слабым функционалом GraphsView. По этой причине я хочу написать х…
PS
Peter Son03 травня 2024 р. 17:57
Best Indian Food Restaurant In Cincinnati OH Ready to embark on a gastronomic journey like no other? Join us at App india restaurant and discover why we're renowned as the Best Indian Food Restaurant In Cincinnati OH . Whether y…
Evgenii Legotckoi
Evgenii Legotckoi02 травня 2024 р. 14:07
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Добрый день. По моему мнению - да, но то, что будет касаться вызовов к функционалу Андроида, может создать огромные трудности.
IscanderChe
IscanderChe30 квітня 2024 р. 04:22
Во Flask рендер шаблона не передаётся в браузер Доброе утро! Имеется вот такой шаблон: <!doctype html><html> <head> <title>{{ title }}</title> <link rel="stylesheet" href="{{ url_…

Слідкуйте за нами в соціальних мережах