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
AD

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

  • Результат:50бали,
  • Рейтинг балів-4
m
  • molni99
  • 26 жовтня 2024 р. 01:37

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

  • Результат:80бали,
  • Рейтинг балів4
m
  • molni99
  • 26 жовтня 2024 р. 01:29

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

  • Результат:20бали,
  • Рейтинг балів-10
Останні коментарі
ИМ
Игорь Максимов22 листопада 2024 р. 11:51
Django - Підручник 017. Налаштуйте сторінку входу до Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii Legotckoi31 жовтня 2024 р. 14:37
Django - Урок 064. Як написати розширення для Python Markdown Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZE19 жовтня 2024 р. 08:19
Читалка файлів fb3 на Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь Максимов05 жовтня 2024 р. 07:51
Django - Урок 064. Як написати розширення для Python Markdown Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas505 липня 2024 р. 11:02
QML - Урок 016. База даних SQLite та робота з нею в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Тепер обговоріть на форумі
Evgenii Legotckoi
Evgenii Legotckoi24 червня 2024 р. 15:11
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey115 листопада 2024 р. 06:04
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProject04 червня 2022 р. 03:49
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9Anonim25 жовтня 2024 р. 09:10
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

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