3.8. Перечисления

3.8. Перечисления

Нередко приходится определять переменную, которая принимает значения из некоего набора. Скажем, файл открывают в любом из трех режимов: для чтения, для записи, для добавления.

Конечно, можно определить три константы для обозначения этих режимов:

const int input = 1;

const int output = 2;

const int append = 3;

и пользоваться этими константами:

bool open_file( string file_name, int open_mode);

// ...

open_file( "Phoenix_and_the_Crane", append );

Подобное решение допустимо, но не вполне приемлемо, поскольку мы не можем гарантировать, что аргумент, передаваемый в функцию open_file() равен только 1, 2 или 3.

Использование перечислимого типа решает данную проблему. Когда мы пишем:

enum open_modes{ input = 1, output, append };

мы определяем новый тип open_modes. Допустимые значения для объекта этого типа ограничены набором 1, 2 и 3, причем каждое из указанных значений имеет мнемоническое имя. Мы можем использовать имя этого нового типа для определения как объекта данного типа, так и типа формальных параметров функции:

void open_file( string file_name, open_modes om );

input, output и append являются элементами перечисления. Набор элементов перечисления задает допустимое множество значений для объекта данного типа. Переменная типа open_modes (в нашем примере) инициализируется одним из этих значений, ей также может быть присвоено любое из них. Например:

open_file( "Phoenix and the Crane", append );

Попытка присвоить переменной данного типа значение, отличное от одного из элементов перечисления (или передать его параметром в функцию), вызовет ошибку компиляции. Даже если попробовать передать целое значение, соответствующее одному из элементов перечисления, мы все равно получим ошибку:

// ошибка: 1 не является элементом перечисления open_modes

open_file( "Jonah", 1 );

Есть способ определить переменную типа open_modes, присвоить ей значение одного из элементов перечисления и передать параметром в функцию:

open_modes om = input;

// ...

om = append;

open_file( "TailTell", om );

Однако получить имена таких элементов невозможно. Если мы напишем оператор вывода:

cout input " " om endl;

то все равно получим:

1 3

Эта проблема решается, если определить строковый массив, в котором элемент с индексом, равным значению элемента перечисления, будет содержать его имя. Имея такой массив, мы сможем написать:

cout open_modes_table[ input ] " "

open_modes_table[ om ] endl

Будет выведено:

input append

Кроме того, нельзя перебрать все значения перечисления:

// не поддерживается

for ( open_modes iter = input; iter != append; ++inter )

// ...

Для определения перечисления служит ключевое слово enum, а имена элементов задаются в фигурных скобках, через запятую. По умолчанию первый из них равен 0, следующий – 1 и так далее. С помощью оператора присваивания это правило можно изменить. При этом каждый следующий элемент без явно указанного значения будет на 1 больше, чем элемент, идущий перед ним в списке. В нашем примере мы явно указали значение 1 для input, при этом output и append будут равны 2 и 3. Вот еще один пример:

// shape == 0, sphere == 1, cylinder == 2, polygon == 3

enum Forms{ share, spere, cylinder, polygon };

Целые значения, соответствующие разным элементам одного перечисления, не обязаны отличаться. Например:

// point2d == 2, point2w == 3, point3d == 3, point3w == 4

enum Points { point2d=2, point2w, point3d=3, point3w=4 };

Объект, тип которого – перечисление, можно определять, использовать в выражениях и передавать в функцию как аргумент. Подобный объект инициализируется только значением одного из элементов перечисления, и только такое значение ему присваивается – явно или как значение другого объекта того же типа. Даже соответствующие допустимым элементам перечисления целые значения не могут быть ему присвоены:

void mumble() {

Points pt3d = point3d; // правильно: pt2d == 3

// ошибка: pt3w инициализируется типом int

Points pt3w = 3;

// ошибка: polygon не входит в перечисление Points

pt3w = polygon;

// правильно: оба объекта типа Points

pt3w = pt3d;

}

Однако в арифметических выражениях перечисление может быть автоматически преобразовано в тип int. Например:

const int array_size = 1024;

// правильно: pt2w преобразуется int

int chunk_size = array_size * pt2w;