По определению struct - это просто класс, все члены которого общие, то есть
struct s { ...
есть просто сокращенная запись
class s { public: ...
Структуры используются в тех случаях, когда скрытие данных неуместно.
Именованное объединение определяется как struct, в которой все члены имеют один и тот же адрес (см. #с.8.5.13). Если известно, что в каждый момент времени нужно только одно значение из структуры, то объединение может сэкономить пространство. Например, можно определить объединение для хранения лексических символов C компилятора:
union tok_val { char* p; // строка char v[8]; // идентификатор (максимум 8 char) long i; // целые значения double d; // значения с плавающей точкой };
Сложность состоит в том, что компилятор, вообще говоря, не знает, какой член используется в каждый данный момент, поэтому надлежащая проверка типа невозможна. Например:
void strange(int i) { tok_val x; if (i) x.p = "2"; else x.d = 2; sqrt(x.d); // ошибка если i != 0 }
Кроме того, объединение, определенное так, как это, нельзя инициализировать. Например:
tok_val curr_val = 12; // ошибка: int присваивается tok_val'у
является недопустимым. Для того, чтобы это преодолеть, можно воспользоваться конструкторами:
union tok_val { char* p; // строка char v[8]; // идентификатор (максимум 8 char) long i; // целые значения double d; // значения с плавающей точкой
tok_val(char*); // должна выбрать между p и v tok_val(int ii) { i = ii; } tok_val() { d = dd; } };
Это позволяет справляться с теми ситуациями, когда типы членов могут быть разрешены по правилам для перегрузки имени функции (см. и #6.3.3). Например:
void f() { tok_val a = 10; // a.i = 10 tok_val b = 10.0; // b.d = 10.0 }
Когда это невозможно (для таких типов, как char* и char[8], int и char, и т.п.), нужный член может быть найден только посредством анализа инициализатора в ходе выполнения или с помощью задания дополнительного параметра. Например:
tok_val::tok_val(char* pp) { if (strlen(pp)
Таких ситуаций вообще-то лучше избегать.