Об’єднання – це особливий тип даних, який дає змогу записувати в одну і ту ж встановлену ділянку оперативної пам'яті дані різних типів і розмірів. Таким чином створюється ділянка пам'яті спільного користування, до якої можна звертатись рідними способами через одну змінну, що має тип об’єднання.
Оголошення об’єднання подібне до оголошення структури: задається шаблон об’єднання і перелічуються відповідні змінні. Так само, як і для структур, шаблон об’єднання можна оголошувати окремо або одночасно з оголошенням змінних. У разі автономного оголошення шаблону об’єднання застосовують синтаксичну конструкцію, аналогічну до шаблону структури, але починають її ключовим словом union:
union тег_ об’єднання {
тип_поля_1 ім’я_поля_1;
тип_поля_2 ім’я_поля_2;
тип_поля_3 ім’я_поля_3;
…
};
Тег об’єднання ідентифікує дане об’єднання, а список полів задає перелік даних, які можна заносити в це об’єднання.
Як і у випадку структур, змінні з типом об’єднання можна оголошувати спільно з оголошенням шаблону або пізніше, використовуючи тип union тег об’єднання. Обсяг ділянки пам'яті, яка виділяється для кожної змінної, що має тип об’єднання, визначається розміром найдовшого поля даного об’єднання.
#include <iostream>
using namespace std;
int main() {
union demo {
double d;
int i;
char c;
};
union demo d;
d.d = 1.2;
cout << d.d << endl; // 1.2
cout << d.i << endl; // 858993459
cout << d.c << endl; // 3
}
Всі три ділянки змінної d займають спільну ділянку оперативної пам'яті, обсяг якої буде дорівнювати розміру найдовшого поля. Якщо розмір змінних типів double, int i char складає 8, 4 і 1 байт відповідно, то розмір змінних типу union demo буде 8 байт. Всі три ділянки мають однакове нульове зміщення відносно адреси початку ділянки, яку займає змінна.
При оголошенні змінних з типом об’єднання можна виконувати ініціалізацію. Але ініціалізувати об’єднання можна значенням, сумісним з типом першого поля цього об’єднання. Наприклад:
#include <iostream>
using namespace std;
int main() {
typedef union demo {
double d;
int i;
char c;
} TEST;
TEST d = { 2.3 };
cout << d.d << endl;
}
До змінних з типом об’єднання можна застосовувати всі операції, що й до структурних змінних: присвоювання (тобто копіювання цілого об’єднання), визначення адреси змінної, виділення окремих елементів (полів) об’єднання. Для звертання до полів об’єднання використовують такі ж синтаксичні конструкції на основі операцій «крапка» і «стрілка», як і в разі звертання до елементів структур:
ім’я_змінної_об’єднання.ім’я_поля
вказівник_на_об’єднання->ім’я_поля
Поля об’єднань можуть бути довільного типу – простого чи складеного. Зокрема, поле об’єднання може бути структурою, вкладеним об’єднанням або вказівником на структуру чи об’єднання. Водночас об’єднання можуть бути елементами масиву або складовими частинами структур інших типів.
В якості приклада розглянемо таку програму. Необхідно зберігати інформацію про платіж, який може здійснюватися кредитною карткою або чеком. Для збереження інформації про платіж кредитною карткою необхідний масив символів з 25 елементів, для збереження інформації про платіж чеком – довге ціле. Двома методами одночасно платіж здійснений не може бути, тому можна використовувати об’єднання:
union payment {
char card[ 25 ];
long check;
};
В кожен момент часу в змінній типу об’єднання зберігається тільки одне значення, відповідальність за його правильне використання лежить на програмісті. Тому зручно разом з об’єднаннями зберігати інформацію про те, який саме елемент використовується в даний момент.
#include <iostream>
using namespace std;
int main() {
enum paytype { CARD, CHECK };
struct payment {
paytype ptype;
union {
char card[ 25 ];
long check;
};
};
payment p;
p.ptype = CHECK;
p.check = 123456;
switch ( p.ptype ) {
case CARD: cout << p.card << endl; break;
case CHECK: cout << p.check << endl; break;
}
}
В цьому прикладі використовується об’єднання без імені, тому для звертання до його полів необхідно просто вказувати імена цих полів.