Объединения
Объединение (union) – это производный тип, синтаксис которого такой же, как и у структур, за исключением того, что ключевое слово union заменяет struct. Члены объединения совместно используют память, и их знанения перекрываются. То есть объявление объединения позволяет интерпретировать его значение как набор типов входящих в него членов. Объединение инициализируется значением в фигурных скобках, причем оно присваивается первому члену объединения. Рассмотрим следующее объявление:
union int_dbl {
int i;
double x;
} n= {0}; // член i инициализован нулем
Переменная n может использоваться или как целая, или как переменная с двойной точностью.
n. i = 7; // в n хранится целое значение 7
cout << n. i << “целое.”;
cout << n. x << “двойной точности – зависит от машины.”;
n. x = 7.0; // значение с двойной точностью
// в n хранится 7.0
Этот пример также иллюстрирует, почему объединения могут быть опасны и часто машинно-зависимы. На некоторых системах не все битовые комбинации являются допустимыми значениями перекрывающихся типов. В этом случае верное значение одного типа, при обращении к нему как к другому типу, может возбудить исключение.
Объединение может быть безымянным, как в следующем фрагменте:
enum week { sun, mon, tues, weds, thurs, fri, sat } ;
// дни недели
union {
int i;
week w;
};
i = 5;
if (w = = sat | | w = = sun) // суббота или воскресенье
cout << “Выходной! “ ;
Объявление безымянного объединения позволяет использовать идентификаторы отдельных членов как переменные. Имена членов должны быть уникальными в пределах области видимости; кроме того, никакие другие переменные безымянного типа объявлять нельзя. Отметьте, что безымянное объединение, объявленное в области видимости файла, должно быть статическим (static).
Усовершенствуем стек, переделав его в виде объединения различных типов. Суть в том, что стек является структурой данных, полезной для хранения значений любых типов. Такой стек можно использовать для обработки разнородных значений, применяя метод LIFO:
// Стек, усовершенствованный для хранения многих типов
enum type { int_type, dbl_type, chr_type, str_type } ;
union rainbow {
int i;
double x;
char c;
char* p;
};
struct rbdata {
type t;
rainbow d;
};
struct u_stack {
rbdata s [max_len];
int top;
};
Структура данных u_stack более гибкая, за что приходится расплачиваться дополнительной памятью, которая требуется для хранения переменной t типа type для каждого из элементов. Операции стека кодируются так же, как в ch_stack. Например, push ( ):
void push (u_stack* stk, str [i++])
{
stk -> s [++stk -> top = c;
}
При использовании u_stack член t отслеживает, значение какого типа содержится в каждом элементе стека. В случае с разнородными данными, вероятно, подошли бы операторы switch, как в следующем примере:
rdata x;
u_stack * pa;
. . . . . . .
x = pop (pa);
switch (x. t) { // извлечение значения правильного типа
case int_type:
v = x. d. i; // v – целое
break ;
case dbl_type:
y = x. d. i; // y – двойной точности
break ;
. . . . . .
}
Однако такой подход неуклюж и ведет к ошибкам. Неточность в задании t приведет к трудно определяемым ошибкам на этапе выполнения. ООП – средства С++ представляют ряд возможностей, которые позволяют делать те же вещи более удобным и безопасным способом.
Множество научных вычислений нуждается в комплексных числах. Напишем АТД для комплексных чисел.
struct complex {
double real, imag;
};
void assign (complex* pc, double r, double i = 0.0)
{
pc -> real = r;
pc -> imag = i;
}
complex add (complex a, complex b)
{
complex temp;
temp. real = a. real + b. real;
temp. imag = a. imag + b. imag;
return temp;
}
Обратите внимание, что если задан аргумент по умолчанию, то можно присвоить значение с двойной точностью комплексной переменной. В этом случае мнимая часть принимается равной нулю.
В следующем фрагменте складываются действительное и комплексное числа:
double f = 2.5;
complex w, x, z;
assign (&x, 5.5, -3.2); // x = 5.5 – 3.2i
assign (&w, f); // w = 2.5 – 0i
z = add (w, x); // z = 8.0 – 3.2i
В упражнениях с 7 по 9 на стр. 126 вы дополните пакет для работы с комплексными числами другими элементами. Традиционный подход, приведенный выше, неудовлетворителен тем, что не позволяет использовать обычную запись выражения для задания вычисления, такую как
z = f + w; // ООП допускает перегрузку +
Полный набор возможностей ООП допускает естественную запись кода.