Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Срауструп CPP.doc
Скачиваний:
4
Добавлен:
05.11.2018
Размер:
4.95 Mб
Скачать

2.6.2. Объединения

Рассмотрим таблицу имен, в которой каждый элемент содержит имя и

его значение. Значение может задаваться либо строкой, либо целым числом:

struct entry {

char* name;

char type;

char* string_value; // используется если type == 's'

int int_value; // используется если type == 'i'

};

void print_entry(entry* p)

{

switch(p->type) {

case 's':

cout << p->string_value;

break;

case 'i':

cout << p->int_value;

break;

default:

cerr << "type corrupted\n";

break;

}

}

Поскольку переменные

string_value и int_value никогда не могут использоваться одновременно,

очевидно, что часть памяти пропадает впустую. Это можно легко исправить,

описав обе переменные как члены объединения, например, так:

struct entry {

char* name;

char type;

union {

char* string_value; // используется если type == 's'

int int_value; // используется если type == 'i'

};

};

Теперь гарантируется, что при выделении памяти для entry члены

string_value и int_value будут размещаться с одного адреса, и

при этом не нужно менять все части программы, работающие с entry.

Из этого следует, что все члены объединения вместе занимают такой же

объем памяти, какой занимает наибольший член объединения.

Надежный способ работы с объединением заключается в том, чтобы

выбирать значение с помощью того же самого члена, который его записывал.

Однако, в больших программах трудно гарантировать, что объединение

используется только таким способом, а в результате использования

не того члена обЪединения могут возникать трудно обнаруживаемые ошибки.

Но можно встроить объединение в такую структуру, которая обеспечит

правильную связь между значением поля типа и текущим типом члена

объединения ($$5.4.6).

Иногда объединения используют для "псевдопреобразований" типа

(в основном на это идут программисты, привыкшие к языкам, в которых

нет средств преобразования типов, и в результате приходится обманывать

транслятор). Приведем пример такого "преобразования" int в int*

на машине VAX, которое достигается простым совпадением разрядов:

struct fudge {

union {

int i;

int* p;

};

};

fudge a;

a.i = 4095;

int* p = a.p; // некорректное использование

В действительности это вовсе не преобразование типа, т.к. на одних

машинах int и int* занимают разный объем памяти, а на других целое

не может размещаться по адресу, задаваемому нечетным числом. Такое

использование объединений не является переносимым, тогда как

существует переносимый способ задания явного преобразования

типа ($$3.2.5).

Иногда объединения используют специально, чтобы избежать

преобразования типов. Например, можно использовать fudge, чтобы

узнать, как представляется указатель 0:

fudge.p = 0;

int i = fudge.i; // i необязательно должно быть 0

Объединению можно дать имя, то есть можно сделать его

полноправным типом. Например, fudge можно описать так:

union fudge {

int i;

int* p;

};

и использовать (некорректно) точно так же, как и раньше. Вместе с тем,

поименованные объединения можно использовать и вполне корректным

и оправданным способом (см. $$5.4.6).