Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
+ООП_Навч_посібник.doc
Скачиваний:
8
Добавлен:
01.07.2025
Размер:
6.58 Mб
Скачать

10.5. Особливості використання об'єднань

10.5.1. Оголошення об'єднання

Об'єднання складається з декількох змінних, які розділяють між собою одну і ту саму область пам'яті. Це означає, що об'єднання забезпечує можливість інтерпретації однієї і тієї ж самої конфігурації бітів двома (або більше) різними способами. Оголошення об'єднання, як неважко переконатися на наведеному нижче прикладі, є подібним до оголошення структури:

union demoUnion { // Оголошення типу об'єднання

short int izm;

char ch;

};

У наведеному прикладі оголошується об'єднання, у якому значення типу short int і значення типу char розділяють одну і ту саму область пам'яті. Необхідно відразу ж з'ясувати один момент: неможливо зробити так, щоб це об'єднання зберігало і цілочисельне значення, і символ одночасно, оскільки змінні izm та ch накладаються (у пам'яті) одне на друге. Але програма у будь-який момент може обробляти інформацію, що міститься у цьому об'єднанні, як цілочисельне значення або як символ. Отже, об'єднання забезпечує два (або більше) способи представлення однієї і тієї ж самої порції даних. Як видно з цього прикладу, об'єднання оголошується за допомогою ключового слова union.

Оголошення об'єднання починається з ключового слова union.

Як і під час оголошення структур, так і під час оголошення об'єднання не визначається жодна змінна. Змінну можна визначити, розмістивши її ім'я в кінці оголошення або скориставшись окремою настановою визначення. Щоб визначити змінну об'єднання іменем uVar типу demoUnion, достатньо записати так:

demoUnion uVar;

У змінній об'єднання uVar як змінна izm типу short int, так і символьна змінна ch розділяють одну і ту саму область пам'яті1. Як змінні izm та ch розділяють одну область пам'яті, показано на рис. 10.2:

Рис. 10.2. Змінні izm та ch разом використовують об'єднання uVar

Під час оголошення об'єднання компілятор автоматично виділяє область пам'яті, достатню для зберігання в об'єднанні змінних найбільшого за об'ємом типу.

Щоб отримати доступ до елемента об'єднання, використовують такий самий синтаксис, який застосовується і для структур: оператори "крапка" і "стрілка". При безпосередньому зверненні до об'єднання (або за допомогою посилання) використовують оператор "крапка". Якщо ж доступ до змінної об'єднання здійснюється через покажчик, використовують оператор "стрілка". Наприклад, щоб присвоїти букву "А" елемента ch об'єднання uVar, достатньо використати таку настанову:

uVar.ch = "А";

У наведеному нижче прикладі функції fun_c1() передається посилання на об'єднання uVar. У тілі цієї функції за допомогою покажчика змінної izm присвоюється значення 10:

fun_c1(&uVar); // Передаємо функції fun_c1() покажчик

// на об'єднання uVar.

//...

}

void fun_c1(demoUnion *un)

{

un->izm =10; // Присвоюємо число 10 члену

// об'єднання uVar за допомогою покажчика.

}

Оскільки об'єднання дають змогу складеній програмі інтерпретувати одні і ті ж самі дані по-різному, то вони часто використовують у випадках, коли потрібне незвичайне перетворення типів. Наприклад, наведений нижче код програми використовує об'єднання для перестановки двох байтів, які становлять коротке цілочисельне значення. Тут для відображення вмісту цілочисельних змінних використовується функція disp_binary(), розроблена в розд. 92.

Код програми 10.8. Демонстрація механізму використання об'єднання для перестановки двох байтів, які становлять коротке цілочисельне значення

#include <vcl>

#include <iostream> // Для потокового введення-виведення

#include <conio> // Для консольного режиму роботи

using namespace std; // Використання стандартного простору імен

void disp_binary(unsigned u);

union swapBytes { // Оголошення типу об'єднання

short int num;

char ch[2];

};

int main()

{

swapBytes Sb_un;

char tmp;

Sb_un.num = 15; // Двійковий код: 0000 0000 0000 1111

cout << "Початкові байти:";

disp_binary(Sb_un.ch[1]);

cout << " ";

disp_binary(Sb_un.ch[0]);

cout << "\n\n";

// Обмін байтів.

tmp = Sb_un.ch[0];

Sb_un.ch[0] = Sb_un.ch[1];

Sb_un.ch[1] = tmp;

cout << "Байти після перестановки:";

disp_binary(Sb_un.ch[1]);

cout << " ";

disp_binary(Sb_un.ch[0]);

cout << "\n\n";

getch(); return 0;

}

// Відображення бітів, з яких складається байт.

void disp_binary(unsigned u)

{

register int t;

for(t=128; t>0; t=t/2)

if(u & t) cout << "1 ";

else cout << "0 ";

}

У процесі виконання програма відображає на екрані такі результати:

Початкові байти : 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1

Байти після перестановки: 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0

У цій програмі цілочисельній змінній Sb_un.num присвоюється число 15. Перестановка двох байтів, що становлять це значення, виконується шляхом обміну двох символів, які утворюють масив ch. Як наслідок, старший і молодший байти цілочисельної змінної num міняються місцями. Ця операція можлива тільки тому, що як змінна num, так і масив ch розділяють одну і ту саму область пам'яті.

У наведеному нижче коді програми продемонстровано ще один приклад використання об'єднання. Тут об'єднання пов'язуються з бітовими полями, що використовуються для відображення в двійковій системі числення ASCII-коду програми, які генерується при натисненні будь-якої клавіші. Ця програма також демонструє альтернативний спосіб відображення окремих бітів, що становлять байт. Об'єднання дає змогу присвоїти значення натиснутої клавіші символьної змінної, а бітові поля використовуються для відображення окремих бітів.

Код програми 10.9. Демонстрація відображення ASCII-коду програми символів у двійковій системі числення

#include <vcl>

#include <iostream> // Для потокового введення-виведення

#include <conio> // Для консольного режиму роботи

using namespace std; // Використання стандартного простору імен

// Бітові поля, які будуть розшифровані.

struct byteStruct { // Оголошення типу структури

unsigned a : 1;

unsigned b : 1;

unsigned c : 1;

unsigned d : 1;

unsigned e : 1;

unsigned f : 1;

unsigned g : 1;

unsigned h : 1;

};

union bitsUnion { // Оголошення типу об'єднання

char ch;

struct byteStruct bit;

} ascii;

void disp_bits(bitsUnion b);

int main()

{

do {

cin >> ascii.ch;

cout << ": ";

disp_bits(ascii);

} while(ascii.ch !='q'); // Вихід при введенні букви 'q'.

getch(); return 0;

}

// Відображення конфігурації бітів для кожного символу.

void disp_bits(bitsUnion B_un)

{

if(B_un.bit.h) cout << "1 ";

else cout << "0 ";

if(B_un.bit.g) cout << "1 ";

else cout << "0 ";

if(B_un.bit.f) cout << "1 ";

else cout << "0 ";

if(B_un.bit.e) cout << "1 ";

else cout << "0 ";

if(B_un.bit.d) cout << "1 ";

else cout << "0 ";

if(B_un.bit.c) cout << "1 ";

else cout << "0 ";

if(B_un.bit.b) cout << "1 ";

else cout << "0 ";

if(B_un.bit.a) cout << "1 ";

else cout << "0 ";

cout << "\n";

}

Ось як буде виглядати один з можливих варіантів виконання цієї програми.

а: 0 1 1 0 0 0 0 1

b: 0 1 1 0 0 0 1 0

c: 0 1 1 0 0 0 1 1

d: 0 1 1 0 0 1 0 0

е: 0 1 1 0 0 1 0 1

f: 0 1 1 0 0 1 1 0

g: 0 1 1 0 0 1 1 1

h: 0 1 1 0 1 0 0 0

i: 0 1 1 0 1 0 0 1

j: 0 1 1 0 1 0 1 0

k: 0 1 1 0 1 0 1 1

l: 0 1 1 0 1 1 0 0

m: 0 1 1 0 1 1 0 1

n: 0 1 1 0 1 1 1 0

o: 0 1 1 0 1 1 1 1

p: 0 1 1 1 0 0 0 0

q: 0 1 1 1 0 0 0 1

Важливо! Оскільки об'єднання припускає, що декілька змінних розділяють одну і ту саму область пам'яті, то цей засіб дає змогу програмісту зберігати інформацію, яка (залежно від ситуації) може містити різні типи даних, і отримувати доступ до цієї інформації. По суті, об'єднання забезпечують низькорівневу підтримку принципів поліморфізму. Іншими словами, об'єднання забезпечує єдиний інтерфейс для декількох різних типів даних, втілюючи таким чином концепцію "один інтерфейс – багато методів" у своїй найпростішій формі.