Объединение представляет собой область памяти, которую разделяют сразу несколько переменных. Оно объявляется подобно структуре или классу с использованием ключевого слова union. Например:
union ex {
unsigned long l;
unsigned short s;
};
Здесь создается тип данных ex – объединение, состоящее из двух элементов – l и s. Для хранения этих элементов будет использована одна область памяти, т.е. их значения в памяти будут накладываться друг на друга. Память под объединение выделяется в соответствии с размером максимального входящего в него типа данных. Так как тип long занимает 4 байта в памяти, а тип short – 2, то под объединение типа ex будет выделено 4 байта. Схематически это можно изобразить следующим образом:
Как видно из схемы, l занимает всю память, выделенную под объединение. А s размещается в её младших 2х байтах (в little-ednian процессорах, к которым относится и архитектура Intel x86, младшие байты в многобайтовых числах располагаются справа, а старшие – слева). Таким образом, 2 младших байта памяти объединения являются общими для его элементов.
Пример, демонстрирующий работу с объединением:
cout.flags(ios::hex | ios::showbase | ios::uppercase); // Для удобного вывода
Как видно из примера, записав в l значение 0xFF0000FF, мы автоматически изменили значение s, которое разделяет с l 2 младших байта. И изменив затем значение s на 0xFF00, мы при этом поменяли младшие 2 байта в l, оставив старшие байты без изменения.
Как и структура, объединение унаследовано от языка C, поэтому все его члены являются открытыми. В C++ объединение поддерживает большинство объектно-ориентированных возможностей, присущих классам: оно может иметь функции-члены (методы), конструкторы, деструкторы и т.д.
В C++ существует возможность создавать анонимные объединения, для которых не указывается название создаваемого типа данных. Так как новый тип данных не создается, то и объявить переменную такого типа невозможно. Вместо этого элементы объединения используются как обычные переменные. Анонимное объединение говорит компилятору, что элементы объединения будут разделять одну область памяти. Например:
union {
unsigned short s;
unsigned long l;
};
l = 0xFF0000FF;
cout << l << ' ' << s << endl;
s = 0xFF00;
cout << l << ' ' << s << endl;
Результат выполнения этого кода аналогичен результату предыдущего примера.
Объединения обычно используются для различного представления одного и того же элемента данных. Например, для представления цвета в компьютерах используется цветовая модель RGBA. Она предполагает, что любой цвет состоит из 4х компонентов: красного (Red), зеленого (Green), синего (Blue) и коэффициента непрозрачности (Alpha). Каждое из составляющих цвета занимает в памяти 1 байт, поэтому сам цвет кодируется 4 байтами, т.е. 32 битами. Для представления такого цвета в программе удобно использовать объединение следующего вида:
union color32 {
struct {
unsigned char Red;
unsigned char Green;
unsigned char Blue;
unsigned char Alpha;
};
unsigned int Color;
};
color32 col;
// Белый цвет - отсутствие всех цветов, полностью непрозрачный
col.Color = 0x000000FF; // col = 00 00 00 FF
// Зеленый цвет
col.Green = 255; // col = 00 FF 00 FF
// Желтый цвет - смесь зеленого и красного
col.Red = 255; // col = FF FF 00 FF
Объединение color32 состоит из 2х элементов – беззнакового int (4 байта) и структуры, состоящей из 4х полей каждое размером в 1 байт. Поля структуры размещаются в памяти в порядке их объявления. Таким образом, из программы мы можем получать доступ как к отдельным компонентам цвета – байтам Red, Green, Blue и Alpha, так и к значению цвета целиком – Color.