enum #
Есть ещё один способ создания именованных констант - enum
(enumeration, перечисление). Он позволяет не просто создать список констант, но и автоматически присвоить им численные значения с шагом 1
, начиная с 0
. Синтаксис такой:
enum имя_списка {
константа1, // значение 0
константа2, // значение 1
константа3, // значение 2
// ...
};
Такая запись создаёт новый тип данных, никакие переменные в этот момент не создаются и память не выделяется. Объявить сам enum
можно где угодно: глобально, внутри функции или внутри класса.
После объявления enum
можно создавать переменные и константы этого нового типа по его названию. Эти переменные могут принимать указанные в списке значения:
имя_списка переменная = константа2;
const имя_списка константа = константа3;
Так как значения выдаются автоматически, нам не нужно думать о нумерации или порядке констант при изменении списка: добавлении новых констант в середине-начале-конце, при удалении или изменении их порядка. В самой программе мы будем оперировать именами констант, а не их численными значениями.
Тип данных #
По умолчанию под капотом нашего нового типа данных находится int
:
enum Foo {
foo1,
foo2,
};
sizeof(Foo); // соответствует int (2/4 байта)
Foo foo = foo1;
sizeof(foo); // соответствует int (2/4 байта)
Можно указать конкретный тип вручную как enum имя_списка : тип
:
enum Foo : uint8_t {
foo1,
foo2,
};
sizeof(Foo); // 1 байт
Foo foo = foo1;
sizeof(foo); // 1 байт
Указание значения #
Значения констант в enum
можно указывать явно, например кодов цветов:
- Если константа не задана явно, её значение будет равно значению предыдущей константы
+ 1
- Имена должны быть уникальными в рамках перечисления
- Имена подчиняются правилам создания имён
enum Colors {
Red = 0xcb2839,
Orange = 0xd55f30,
Yellow = 0xd69d27,
color1, // 0xd69d28 = Yellow + 1
color2, // 0xd69d29 = color1 + 1
color3 = 1234,
color4, // 1235
//color4, // ошибка, такое имя уже есть
//for, // ошибка, ключевое слово
};
Компилятор автоматически повысит тип данных, если хоть одно значение выходит за int
- тип Colors
из примера выше является 4-байтным, т.к. 2 байта не хватило бы для хранения указанных чисел (для AVR, int 2 байта)
sizeof(Colors); // 4 байта
Подсказки значений #
К константе можно обратиться как напрямую - константа
, так и через имя списка при помощи оператора ::
- имя_списка::константа
:
enum Colors {
Red = 0xcb2839,
Orange = 0xd55f30,
Yellow = 0xd69d27,
};
Colors color = Colors::Red;
IDE, в которых есть автодополнение кода, подскажут список констант при вводе имя_списка::
:
Пересечение имён #
Константы обычного enum
доступны глобально для всего кода как обычные числа, их имена могут пересекаться с остальными именами переменных в программе:
int Red; // #1
enum Colors {
Red, // ошибка, имя уже занято переменной #1 выше
Orange,
Yellow, // #2
};
int Yellow; // ошибка, имя занято #2 в enum выше
Константы enum
могут преобразовываться в соответствующие им числа:
int color1 = Orange; // 1
int color2 = Colors::Orange; // 1
В крупном проекте или при использовании в библиотеках очень сложно соблюдать уникальность имён констант - приходится добавлять им некрасивые префиксы. Для решения этой проблемы существует enum class
.
Enum class #
Это тот же enum
, но его константы:
- "Спрятаны" (инкапсулированы) внутри имени типа, обратиться к ним можно только через
::
- Не пересекаются с остальной программой
- Не могут быть автоматически преобразованы и присвоены к обычным переменным. Принудительно - могут
int Red;
enum class Colors {
Red, // ошибки нет
Orange,
Yellow,
};
int Yellow; // ошибки нет
int color1 = Orange; // ошибка, такой константы нет
int color2 = Colors::Orange; // ошибка, нельзя присвоить к int
int color3 = (int)Colors::Orange; // так можно, но не нужно
Colors color3 = Colors::Orange; // нужно так
Если нужно указать конкретный тип такому перечислению - это делается так:
enum class Colors : uint32_t {
// ...
};
Рекомендуется использовать enum class
- он удобнее и безопаснее, сводит количество возможных конфликтов имён практически в ноль
Количество констант #
В C/C++ невозможно узнать из программы количество констант, которое задано в enum
или enum class
. Но есть трюк, который работает, если все константы нумеруются автоматически с нуля (без задания вручную) - численное значение последней константы всегда равно количеству остальных констант! Просто создаём в конце константу с именем, которое олицетворяет длину списка, и можно пользоваться:
enum class MyConst {
someConst, // 0
const2, // 1
Red, // 2
green, // 3
_len, // 4 - количество констант (не включая эту)
};
int enumLen = (int)MyConst::_len; // == 4