Реклама

C++ - Урок 008. Перечисления

c++, enum, enumerations

В дополнение к классам C++ поддерживает перечисления. В современном стандарте C++ поддерживаются как перечисления без области видимости, которые были введены в ранних версиях C++, а также C.

enum ColorTypes {
    Green,
    Yellow,
    Red
};

Так и перечисления с областью видимости

enum class ColorTypes {
    Green,
    Yellow,
    Red
};

Отличие перечислений с областью видимости от перечислений без области видимости

Отличие перечисление с областью видимостью отличается от перечислении без области видимости тем, что переменные перечислений с областью видимости не могут неявно преобразовываться в целочисленные переменные и обратно. Для такого преобразования следует использовать static_cast<int>().

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;
                         ^

Как задать конкретный целочисленный тип для перечисления

Перечисления также могут иметь более конкретизированный тип, который должен быть определённым целочисленным типом:

  • unsigned char;
  • char;
  • int;
  • long 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;
}

Реклама

Комментарии

Комментарии

Только авторизованные пользователи могут оставлять комментарии.
Пожалуйста, Авторизуйтесь или Зарегистрируйтесь

Qt - Тест 001. Сигналы и слоты

  • Результат 5 баллов
  • Очки рейтинга -10

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

  • Результат 57 баллов
  • Очки рейтинга -2

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

  • Результат 7 баллов
  • Очки рейтинга -10
Последние комментарии
  • EVILEG
  • 7 декабря 2017 г. 9:47

Django - Урок 011. Добавление комментариев на сайт с Django

Визуальный пример чего? комментариев? При ответе на конкретный комментарий рядом с ником отвечающего будет стрелочка и указание ник другого пользователя. Который будет ссылкой на коммента...

  • Bernar
  • 7 декабря 2017 г. 9:24

Django - Урок 011. Добавление комментариев на сайт с Django

есть визуальный пример ?

  • EVILEG
  • 6 декабря 2017 г. 11:30

Django - Урок 011. Добавление комментариев на сайт с Django

Да, так будет даже лучше, я на сайте уже обновил до такого вида код Вот это уже не нужно if request.method == 'POST': Поскольку Вы и так используете метод post, то есть эта про...

  • Bernar
  • 6 декабря 2017 г. 11:19

Django - Урок 011. Добавление комментариев на сайт с Django

сделал немного по другому class EArticleView(View): template_name = 'knowledge/article.html' comment_form = CommentForm def get(self, request, *args, **kwargs): ...

Сейчас обсуждают на форуме
  • Миша
  • 15 декабря 2017 г. 11:26

Как найти в QVector макс и мин

Спасибо

  • Galant
  • 14 декабря 2017 г. 19:58

LPT

Понял! Спасибо!

  • EVILEG
  • 14 декабря 2017 г. 13:38

QCustomPlot можно ли построить прерывистую линию на одном графике?

Во-первых: В pro файле проект по идее достаточно указать следующий define для включения возможности рендеринга через OpenGL DEFINES += QCUSTOMPLOT_USE_OPENGL И во вторых:...

  • EVILEG
  • 13 декабря 2017 г. 8:05

В многопоточности выполнять действие только в одном из потоков

Статическиe методs QThread::currentThread(); и QThread::currentThreadId() могут возвращать указатель на поток и его handle id соответственно. Можете попробовать через как...

  • EVILEG
  • 13 декабря 2017 г. 7:57

А что по поводу авторизации ?

Наличие токена - это правильный подход. Например, у меня на сайте в каждой форме есть токен, чтобы не было возможности подделки запросов. Что касается SSL, то стоит поискать информацию н...