Я узнал, но на самом деле не получаю профсоюзы. Каждый текст на C или С++, который я просматриваю, вводит их (иногда мимолетно), но они, как правило, дают очень мало практических примеров того, почему и где их использовать. Когда союзы будут полезны в современном (или даже в наследство) случае? Мое единственное предположение - программирование микропроцессоров, когда у вас очень ограниченное пространство для работы или когда вы разрабатываете API (или что-то подобное), и вы хотите заставить конечного пользователя иметь только один экземпляр нескольких объектов/типов в один раз. Эти две догадки даже близки к правилу?
Когда кто-нибудь будет использовать союз? Это остаток от дней C?
Ответ 1
Союзы обычно используются с компанией дискриминатора: переменная, указывающая, какая из полей союза действительна. Например, скажем, вы хотите создать свой собственный Variant тип:
struct my_variant_t {
int type;
union {
char char_value;
short short_value;
int int_value;
long long_value;
float float_value;
double double_value;
void* ptr_value;
};
};
Затем вы будете использовать его, например:
/* construct a new float variant instance */
void init_float(struct my_variant_t* v, float initial_value) {
v->type = VAR_FLOAT;
v->float_value = initial_value;
}
/* Increments the value of the variant by the given int */
void inc_variant_by_int(struct my_variant_t* v, int n) {
switch (v->type) {
case VAR_FLOAT:
v->float_value += n;
break;
case VAR_INT:
v->int_value += n;
break;
...
}
}
Это на самом деле довольно распространенная идиома, особенно для внутренних компонентов Visual Basic.
Для реального примера см. SDL SDL_Event union. (фактический исходный код здесь). В верхней части объединения есть поле type
, и одно и то же поле повторяется на каждой структуре событий SDL_ *. Затем, чтобы обработать правильное событие, вам нужно проверить значение поля type
.
Преимущества просты: существует один тип данных для обработки всех типов событий без использования лишней памяти.
Ответ 2
Я нахожу, что С++-объединения довольно круто. Похоже, что люди обычно думают только о прецеденте, когда человек хочет изменить значение единственного экземпляра "на месте" (который, кажется, служит только для сохранения памяти или выполнения сомнительных преобразований).
Фактически, союзы могут иметь большую силу в качестве инструмента для разработки программного обеспечения, , даже если вы никогда не меняете значение любого экземпляра union.
Использовать случай 1: хамелеон
С объединениями вы можете перегруппировать несколько произвольных классов под одним наименованием, которое не имеет сходства с случаем базового класса и его производных классов. Однако какие изменения вы можете и не можете сделать с данным экземпляром union:
struct Batman;
struct BaseballBat;
union Bat
{
Batman brucewayne;
BaseballBat club;
};
ReturnType1 f(void)
{
BaseballBat bb = {/* */};
Bat b;
b.club = bb;
// do something with b.club
}
ReturnType2 g(Bat& b)
{
// do something with b, but how do we know what inside?
}
Bat returnsBat(void);
ReturnType3 h(void)
{
Bat b = returnsBat();
// do something with b, but how do we know what inside?
}
Похоже, что программист должен быть уверен в типе содержимого данного профсоюзного экземпляра, когда хочет его использовать. Это имеет место в функции f
выше. Однако, если функция должна была получить экземпляр union в качестве переданного аргумента, как в случае с g
выше, то он не знал, что с ним делать. То же самое относится к функциям, возвращающим экземпляр объединения, см. h
: как вызывающий абонент знает, что внутри?
Если экземпляр union никогда не передается в качестве аргумента или как возвращаемое значение, он должен иметь очень монотонную жизнь, с волнами возбуждения, когда программист выбирает изменить его содержимое:
Batman bm = {/* */};
Baseball bb = {/* */};
Bat b;
b.brucewayne = bm;
// stuff
b.club = bb;
И это самый (un) популярный случай союзов. Другой случай использования - когда экземпляр объединения приходит с чем-то, что сообщает вам его тип.
Используйте случай 2: "Приятно познакомиться, я object
, от Class
"
Предположим, что программист, выбранный для того, чтобы всегда сочетать экземпляр объединения с дескриптором типа (я оставлю его на усмотрение читателя, чтобы представить реализацию для одного такого объекта). Это побеждает цель самого союза, если то, что хочет программист, - это сохранить память и что размер дескриптора типа не является незначительным по отношению к объему объединения. Но пусть предположим, что важно, чтобы экземпляр union мог быть передан в качестве аргумента или как возвращаемое значение с вызывающим или вызывающим, не зная, что внутри.
Затем программист должен написать инструкцию потока управления switch
, чтобы рассказать Брюсу Уэйн отдельно от деревянной палочки или что-то подобное. Это не так уж плохо, когда в объединении есть только два типа содержимого, но, очевидно, объединение больше не масштабируется.
Пример использования 3:
Как авторы рекомендации по стандарту ISO С++ вернули его в 2008 году,
Для многих важных проблемных областей требуется либо большое количество объектов, либо ограниченная память Ресурсы. В этих ситуациях сохранение пространства очень важно, и объединение часто является идеальным способом сделать это. Фактически, распространенным случаем является ситуация, когда профсоюз никогда не меняет своего активного члена в течение его жизни. Он может быть сконструирован, скопирован и разрушен, как если бы он был структурой, содержащей только один элемент. Типичным применением этого было бы создание гетерогенного набора несвязанных типов, которые не распределены динамически (возможно, они созданы на месте на карте или в элементах массива).
И теперь, пример, с диаграммой классов UML:
Ситуация на простом английском языке: объект класса A может иметь объекты любого класса среди B1,..., Bn и не более одного из каждого типа с n является довольно большим числом, скажем, не менее 10.
Мы не хотим добавлять поля (элементы данных) в так:
private:
B1 b1;
.
.
.
Bn bn;
потому что n может меняться (мы можем добавить классы Bx в микс), и потому что это вызовет беспорядок с конструкторами и потому, что объекты A занимают много места.
Мы могли бы использовать сумасшедший контейнер указателей void*
для Bx
объектов с бросками для их извлечения, но этот неуклюжий и так C-стиль... но что более важно, что оставило бы нас со временем жизни многих динамически распределенных объектов для управления.
Вместо этого можно сделать следующее:
union Bee
{
B1 b1;
.
.
.
Bn bn;
};
enum BeesTypes { TYPE_B1, ..., TYPE_BN };
class A
{
private:
std::unordered_map<int, Bee> data; // C++11, otherwise use std::map
public:
Bee get(int); // the implementation is obvious: get from the unordered map
};
Затем, чтобы получить содержимое экземпляра union из data
, вы используете a.get(TYPE_B2).b2
и подобные, где a
- это экземпляр класса a
.
Это тем более мощно, что союзы не ограничены в С++ 11. Подробнее см. документ, связанный с выше или в этой статье.
Ответ 3
Один пример представлен во встроенной области, где каждый бит регистра может означать что-то другое. Например, объединение 8-битного целого и структура с 8 отдельными 1-битными битовыми полями позволяют либо изменить один бит, либо весь байт.
Ответ 4
Herb Sutter написал в GOTW около шести лет назад, добавлено акцент:
"Но не думайте, что профсоюзы - это только удержание с ранних времен. Профсоюзы, пожалуй, наиболее полезны для экономии места, позволяя перекрывать данные, , и это все еще желательно в С++, а сегодня современный мир. Например, некоторые из самых современных С++ стандартных реализаций библиотек в мире теперь используют именно этот метод для реализации" оптимизации небольших строк" - отличную альтернативу оптимизации, которая повторно использует хранилище внутри строки сам объект: для больших строк пространство внутри строкового объекта хранит обычный указатель на динамически выделенный буфер и служебную информацию, такую как размер буфера; для небольших строк одно и то же пространство используется повторно для хранения содержимого строки напрямую и полностью избегается любое динамическое распределение памяти. Подробнее об оптимизации небольших строк (и других строковых оптимизаций и пессимизации на значительной глубине) см.....
И для менее полезного примера см. длинный, но неубедительный вопрос gcc, строгое сглаживание и кастинг через объединение.
Ответ 5
Ну, один пример использования, о котором я могу думать, это:
typedef union
{
struct
{
uint8_t a;
uint8_t b;
uint8_t c;
uint8_t d;
};
uint32_t x;
} some32bittype;
Затем вы можете получить доступ к 8-битным отдельным частям этого 32-битного блока данных; однако, подготовьтесь к потенциальному укусу по контенту.
Это всего лишь один гипотетический пример, но всякий раз, когда вы хотите разделить данные в поле на такие составные части, вы можете использовать объединение.
Тем не менее, существует также метод, безопасный для всех:
uint32_t x;
uint8_t a = (x & 0xFF000000) >> 24;
Например, поскольку эта двоичная операция будет преобразована компилятором в правильную формулировку.
Ответ 6
Союзы полезны при работе с данными байтового уровня (низкого уровня).
Одним из моих недавних применений было моделирование IP-адресов, которое выглядит следующим образом:
// Composite structure for IP address storage
union
{
// IPv4 @ 32-bit identifier
// Padded 12-bytes for IPv6 compatibility
union
{
struct
{
unsigned char _reserved[12];
unsigned char _IpBytes[4];
} _Raw;
struct
{
unsigned char _reserved[12];
unsigned char _o1;
unsigned char _o2;
unsigned char _o3;
unsigned char _o4;
} _Octet;
} _IPv4;
// IPv6 @ 128-bit identifier
// Next generation internet addressing
union
{
struct
{
unsigned char _IpBytes[16];
} _Raw;
struct
{
unsigned short _w1;
unsigned short _w2;
unsigned short _w3;
unsigned short _w4;
unsigned short _w5;
unsigned short _w6;
unsigned short _w7;
unsigned short _w8;
} _Word;
} _IPv6;
} _IP;
Ответ 7
Некоторые используют для объединений:
- Предоставляет общий интерфейс для неизвестного внешнего хоста.
- Манипулировать данными с плавающей запятой с внешней ЦП, такими как принятие VAX G_FLOATS из сетевой ссылки и преобразование их в IEEE 754 длинные операции для обработки.
- Обеспечьте простой доступ к двоичному типу доступа к типу более высокого уровня.
union { unsigned char byte_v[16]; long double ld_v; }
С помощью этого объявления просто показать шестнадцатеричные байтовые значения
long double
, изменить знак экспоненты, определить, является ли это денормальным значением, или реализовать длинную двойную арифметику для ЦП, который ее не поддерживает, и т.д..
-
Сохранение пространства памяти, когда поля зависят от определенных значений:
class person { string name; char gender; // M = male, F = female, O = other union { date castrated; // for males int pregnancies; // for females } gender_specific_data; }
-
Соедините включенные файлы для использования с вашим компилятором. Вы найдете десятки и сотни видов использования
union
:[[email protected] ~]$ cd /usr/include [[email protected] include]$ grep -w union * a.out.h: union argp.h: parsing options, getopt is called with the union of all the argp bfd.h: union bfd.h: union bfd.h:union internal_auxent; bfd.h: (bfd *, struct bfd_symbol *, int, union internal_auxent *); bfd.h: union { bfd.h: /* The value of the symbol. This really should be a union of a bfd.h: union bfd.h: union bfdlink.h: /* A union of information depending upon the type. */ bfdlink.h: union bfdlink.h: this field. This field is present in all of the union element bfdlink.h: the union; this structure is a major space user in the bfdlink.h: union bfdlink.h: union curses.h: union db_cxx.h:// 4201: nameless struct/union elf.h: union elf.h: union elf.h: union elf.h: union elf.h:typedef union _G_config.h:typedef union gcrypt.h: union gcrypt.h: union gcrypt.h: union gmp-i386.h: union { ieee754.h:union ieee754_float ieee754.h:union ieee754_double ieee754.h:union ieee854_long_double ifaddrs.h: union jpeglib.h: union { ldap.h: union mod_vals_u { ncurses.h: union newt.h: union { obstack.h: union pi-file.h: union { resolv.h: union { signal.h:extern int sigqueue (__pid_t __pid, int __sig, __const union sigval __val) stdlib.h:/* Lots of hair to allow traditional BSD use of `union wait' stdlib.h: (__extension__ (((union { __typeof(status) __in; int __i; }) \ stdlib.h:/* This is the type of the argument to `wait'. The funky union stdlib.h: causes redeclarations with either `int *' or `union wait *' to be stdlib.h:typedef union stdlib.h: union wait *__uptr; stdlib.h: } __WAIT_STATUS __attribute__ ((__transparent_union__)); thread_db.h: union thread_db.h: union tiffio.h: union { wchar.h: union xf86drm.h:typedef union _drmVBlank {
Ответ 8
Пример, когда я использовал объединение:
class Vector
{
union
{
double _coord[3];
struct
{
double _x;
double _y;
double _z;
};
};
...
}
это позволяет мне получать доступ к моим данным в виде массива или элементов.
Я использовал союз, чтобы разные термины указывали на одно и то же значение. При обработке изображений, работал ли я над столбцами или шириной или размером в направлении X, это может запутать. Чтобы решить эту проблему, я использую объединение, поэтому знаю, какие описания идут вместе.
union { // dimension from left to right // union for the left to right dimension
uint32_t m_width;
uint32_t m_sizeX;
uint32_t m_columns;
};
union { // dimension from top to bottom // union for the top to bottom dimension
uint32_t m_height;
uint32_t m_sizeY;
uint32_t m_rows;
};
Ответ 9
Союзы обеспечивают полиморфизм в C.
Ответ 10
Ключевое слово union
, все еще используемое в С++ 03 1 является в основном остатком C-дней. Самая вопиющая проблема заключается в том, что она работает только с POD 1.
Идея объединения, однако, все еще присутствует, и действительно, библиотеки Boost имеют класс, похожий на объединение:
boost::variant<std::string, Foo, Bar>
Что имеет большинство преимуществ union
(если не все) и добавляет:
- способность правильно использовать не-POD-типы
- безопасность статического типа
На практике было продемонстрировано, что она эквивалентна комбинации union
+ enum
и сравнивается с тем, что она была такой же быстрой (в то время как boost::any
больше относится к области dynamic_cast
, так как она использует RTTI).
1 Союзы были обновлены в С++ 11 (неограниченные союзы) и теперь могут содержать объекты с деструкторами, хотя пользователь имеет для вызова деструктора вручную (в текущем активном члене объединения). Гораздо проще использовать варианты.
Ответ 11
Блестящее использование объединения - это выравнивание памяти, которое я нашел в исходном коде PCL (Point Cloud Library). Единая структура данных в API может ориентироваться на две архитектуры: процессор с поддержкой SSE, а также процессор без поддержки SSE. Например, структура данных для PointXYZ равна
typedef union
{
float data[4];
struct
{
float x;
float y;
float z;
};
} PointXYZ;
3 поплавки дополняются дополнительным поплавком для выравнивания SSE. Итак, для
PointXYZ point;
Пользователь может либо получить доступ к point.data [0] или point.x(в зависимости от поддержки SSE) для доступа, скажем, координаты x. Более похожие сведения об использовании более подробно описаны в следующей ссылке: Документация PCL Типы PointT
Ответ 12
Из Статья в Википедии о союзах:
Основная полезность объединения сохранить пространство, поскольку оно обеспечивает способ позволить многим различным типам хранится в том же пространстве. Союзы также обеспечивают сырой полиморфизм. Однако, проверка типов не производится, поэтому до программиста, чтобы быть уверенным что к соответствующим полям различные контексты. Соответствующее поле переменной объединения обычно определяемые состоянием других переменные, возможно, в структура.
В одном идиоме программирования C используется союзы для выполнения того, что С++ вызывает reinterpret_cast, присваивая одному поле союза и чтение из другой, как это делается в коде, который зависит от исходного представления значения.
Ответ 13
Допустим, у вас есть n различных типов конфигураций (просто являющийся набором переменных, определяющих параметры). Используя перечисление типов конфигурации, вы можете определить структуру, которая имеет идентификатор типа конфигурации, а также объединение всех различных типов конфигураций.
Таким образом, везде, где вы передаете конфигурацию, можно использовать идентификатор, чтобы определить, как интерпретировать данные конфигурации, но если конфигурации были огромными, вам не пришлось бы иметь параллельные структуры для каждого потенциального пространства, теряющего пространство.
Ответ 14
Один из последних стимулов для уже поднятой важности профсоюзов был предоставлен Строгое правило псевдонимов, представленное в последней версии C стандарт.
Вы можете использовать союзы для type-punning без нарушения стандарта C.
Эта программа имеет неопределенное поведение (потому что я предположил, что float
и unsigned int
имеют одинаковую длину), но не поведение undefined (см. здесь).
#include <stdio.h>
union float_uint
{
float f;
unsigned int ui;
};
int main()
{
float v = 241;
union float_uint fui = {.f = v};
//May trigger UNSPECIFIED BEHAVIOR but not UNDEFINED BEHAVIOR
printf("Your IEEE 754 float sir: %08x\n", fui.ui);
//This is UNDEFINED BEHAVIOR as it violates the Strict Aliasing Rule
unsigned int* pp = (unsigned int*) &v;
printf("Your IEEE 754 float, again, sir: %08x\n", *pp);
return 0;
}
Ответ 15
Я хотел бы добавить один хороший практический пример для использования union - внедрение калькулятора/интерпретатора формулы или его использование в вычислении (например, вы хотите использовать модифицируемые во время выполнения части ваших вычислительных формул - решение уравнения численно - например). Таким образом, вы можете определить числа/константы разных типов (целочисленные, с плавающей точкой, даже сложные числа), например:
struct Number{
enum NumType{int32, float, double, complex}; NumType num_t;
union{int ival; float fval; double dval; ComplexNumber cmplx_val}
}
Таким образом, вы сохраняете память и, что более важно, избегаете любых динамических распределений, возможно, для экстремального количества (если вы используете множество заданных чисел времени выполнения) для небольших объектов (по сравнению с реализациями через наследование/полиморфизм класса), Но что еще интереснее, вы все еще можете использовать силу полиморфизма С++ (если вы фанат двойной диспетчеризации, например;) с этим типом структуры. Просто добавьте указатель интерфейса "dummy" к родительскому классу всех типов номеров в качестве поля этой структуры, указывая на этот экземпляр вместо/в дополнение к необработанному типу или используя старые добрые указатели функций C.
struct NumberBase
{
virtual Add(NumberBase n);
...
}
struct NumberInt: Number
{
//implement methods assuming Number union contains int
NumberBase Add(NumberBase n);
...
}
struct NumberDouble: Number
{
//implement methods assuming Number union contains double
NumberBase Add(NumberBase n);
...
}
//e.t.c. for all number types/or use templates
struct Number: NumberBase{
union{int ival; float fval; double dval; ComplexNumber cmplx_val;}
NumberBase* num_t;
Set(int a)
{
ival=a;
//still kind of hack, hope it works because derived classes of Number dont add any fields
num_t = static_cast<NumberInt>(this);
}
}
поэтому вы можете использовать полиморфизм вместо проверок типов с ключом (тип) - с реализацией с эффективностью памяти (без динамического выделения небольших объектов) - если вам это нужно, конечно.
Ответ 16
Из http://cplus.about.com/od/learningc/ss/lowlevel_9.htm:
Использование союза невелико и далеко. На большинстве компьютеров размер указателя и int, как правило, одинаковы - это связано с тем, что оба обычно вписываются в регистр в CPU. Поэтому, если вы хотите сделать быстрый и грязный оттенок указателя на int или наоборот, объявите объединение.
union intptr { int i; int * p; }; union intptr x; x.i = 1000; /* puts 90 at location 1000 */ *(x.p)=90;
Другое использование объединения в протоколе команды или сообщения, где отправляются и принимаются сообщения разных размеров. Каждый тип сообщения будет иметь различную информацию, но каждая из них будет иметь фиксированную часть (вероятно, struct) и бит переменной части. Вот как это можно реализовать.
struct head { int id; int response; int size; }; struct msgstring50 { struct head fixed; char message[50]; } struct
struct msgstring80 {struct head fixed; char сообщение [80]; }
struct msgint10 {struct head fixed; int message [10]; } struct msgack {struct head fixed; int ok; } union messagetype {
struct msgstring50 m50; struct msgstring80 m80; struct msgint10 i10; struct msgack ack; }На практике, хотя профсоюзы имеют одинаковый размер, имеет смысл отправлять только значимые данные и не тратить впустую пространство. Msgack просто 16 байтов, а msgstring80 - 92 байта. Поэтому, когда Инициализируется переменная messagetype, у нее есть поле размера поля в зависимости от типа. Это может затем использоваться другими функции для передачи правильного количества байтов.
Ответ 17
Unions предоставляют способ манипулирования различными типами данных в одной области хранения без внедрения какой-либо машинной независимой информации в программе Они аналогичны вариантным записям в pascal
В качестве примера, например, который можно найти в менеджере таблиц символов компилятора, предположим, что константой может быть int, float или указатель на символ. Значение определенной константы должен храниться в переменной соответствующего типа, но наиболее удобно для управления таблицей, если значение занимает один и тот же объем памяти и хранится в одном и том же месте независимо от его типа. Это цель объединения - единственная переменная, которая может законно удерживать любой из нескольких типов. Синтаксис основан на структурах:
union u_tag {
int ival;
float fval;
char *sval;
} u;
Переменная u будет достаточно большой, чтобы удерживать самый большой из трех типов; конкретный размер зависит от реализации. Любой из этих типов может быть присвоен u, а затем использован в выражения, если использование согласовано
Ответ 18
В самые ранние дни C (например, как описано в 1974) все структуры разделяли общее пространство имен для своих членов. Каждое имя участника ассоциировалось с типом и смещением; если "wd_woozle" был "int" при смещении 12, то с указателем p
любого типа структуры p->wd_woozle
был бы эквивалентен *(int*)(((char*)p)+12)
. Язык требовал, чтобы все члены всех типов структур имели уникальные имена, за исключением того, что он явно разрешал повторное использование имен членов в тех случаях, когда каждая структура, в которой они использовались, рассматривала их как общую начальную последовательность.
Тот факт, что типы структуры могут использоваться без раздумий, позволяет создавать структуры, как если бы они содержали перекрывающиеся поля. Например, заданные определения:
struct float1 { float f0;};
struct byte4 { char b0,b1,b2,b3; }; /* Unsigned didn't exist yet */
код может объявить структуру типа "float1", а затем использовать "members" b0... b3 для доступа к отдельным байтам в нем. Когда язык был изменен, чтобы каждая структура получала отдельное пространство имен для своих членов, код, который полагался бы на способность доступа к вещам несколькими способами, мог бы сломаться. Значения разделения пространств имен для разных типов структуры были достаточными, чтобы потребовать, чтобы такой код был изменен для его размещения, но значение таких методов было достаточным, чтобы оправдать расширение языка для продолжения его поддержки.
Код, который был написан для использования возможности доступа к хранилищу в struct float1
, как если бы он был struct byte4
, мог быть создан для работы на новом языке, добавив объявление: union f1b4 { struct float1 ff; struct byte4 bb; };
, объявив объекты как введите union f1b4;
, а не struct float1
, и замените обращения к f0
, b0
, b1
и т.д. с помощью ff.f0
, bb.b0
, bb.b1
и т.д. Хотя есть лучшие способы такого кода возможно, был поддержан, подход union
был, по крайней мере, несколько работоспособен, по крайней мере, с интерпретациями правил псевдонимов C89.