Я научился, но на самом деле не получить союзы. Каждый текст на C или C ++, который я прохожу, представляет их (иногда мимоходом), но они, как правило, дают очень мало практических примеров того, почему или где их использовать. Когда профсоюзы будут полезны в современном (или даже устаревшем) случае? Мои только два предположения будут программировать микропроцессоры, когда у вас очень ограниченное пространство для работы или когда вы разрабатываете API (или что-то подобное), и вы хотите заставить конечного пользователя иметь только один экземпляр из нескольких объектов / типов в один раз. Эти два предположения даже близко к праву?
133
Ответы:
Объединения обычно используются с компанией дискриминатора: переменная, указывающая, какое из полей объединения допустимо. Например, допустим, вы хотите создать свой собственный тип Variant :
Тогда вы будете использовать его как:
На самом деле это довольно распространенная идиома, особенно в Visual Basic.
Для реального примера см. SDL SDL_Event union . ( фактический исходный код здесь ). В
type
верхней части объединения есть поле, и то же поле повторяется в каждой структуре события SDL_ *. Затем, чтобы обработать правильное событие, вам нужно проверить значениеtype
поля.Преимущества просты: существует один тип данных для обработки всех типов событий без использования ненужной памяти.
источник
struct object
в github.com/petermichaux/bootstrap-scheme/blob/v0.21/scheme.cЯ нахожу профсоюзы C ++ довольно крутыми. Кажется, что люди обычно думают только о случае использования, когда кто-то хочет изменить значение экземпляра объединения «на месте» (которое, кажется, служит только для экономии памяти или выполнения сомнительных преобразований).
На самом деле, союзы могут иметь большую силу как инструмент разработки программного обеспечения, даже если вы никогда не меняете ценность какого-либо экземпляра объединения .
Вариант использования 1: хамелеон
С помощью союзов вы можете перегруппировать несколько произвольных классов под одним наименованием, что не лишено сходства со случаем базового класса и его производных классов. Что изменится, однако, это то, что вы можете и не можете делать с данным экземпляром объединения:
Похоже, что программист должен быть уверен в типе содержимого данного экземпляра объединения, когда он хочет его использовать. Это дело в функции
f
выше. Однако, если функция получит экземпляр объединения в качестве переданного аргумента, как в случае сg
выше, то она не будет знать, что с ним делать. То же самое относится и к функциям, возвращающим экземпляр объединения, смотритеh
: как вызывающая сторона узнает, что внутри?Если экземпляр объединения никогда не передается в качестве аргумента или в качестве возвращаемого значения, то он должен иметь очень монотонную жизнь с всплесками волнения, когда программист решает изменить свое содержимое:
И это самый (не) популярный вариант использования союзов. Другой вариант использования - это когда экземпляр объединения приходит вместе с чем-то, что говорит вам о его типе.
Вариант использования 2: «Приятно познакомиться, я
object
изClass
»Предположим, что программист решил всегда связывать экземпляр объединения с дескриптором типа (я оставлю на усмотрение читателя представить реализацию одного такого объекта). Это противоречит цели самого объединения, если программист хочет сохранить память и что размер дескриптора типа не является ничтожным по сравнению с размером объединения. Но давайте предположим, что крайне важно, чтобы экземпляр объединения мог быть передан как аргумент или как возвращаемое значение, когда вызываемый или вызывающий не знает, что находится внутри.
Затем программист должен написать оператор
switch
потока управления, чтобы отличить Брюса Уэйна от деревянной палочки или чего-то подобного. Это не так уж плохо, когда в объединении есть только два типа содержимого, но очевидно, что объединение больше не масштабируется.Вариант использования 3:
Как утверждают авторы рекомендации для стандарта ISO C ++ в 2008 году,
А теперь пример с диаграммой классов UML:
Ситуация на простом английском языке: у объекта класса A могут быть объекты любого класса из B1, ..., Bn и не более одного каждого типа, причем n - довольно большое число, скажем, по меньшей мере 10.
Мы не хотим добавлять поля (элементы данных) в A следующим образом:
потому что n может варьироваться (мы могли бы добавить классы Bx в смесь), и потому что это могло бы вызвать беспорядок с конструкторами и потому что объекты A занимали бы много места.
Мы могли бы использовать дурацкий контейнер
void*
указателей наBx
объекты с приведениями для их извлечения, но это глупо и так в стиле C ... но что более важно, это оставило бы нам время жизни многих динамически распределенных объектов для управления.Вместо этого можно сделать следующее:
Затем, чтобы получить содержимое экземпляра объединения
data
, вы используетеa.get(TYPE_B2).b2
и лайки, гдеa
находитсяA
экземпляр класса .Это тем более мощно, так как союзы не ограничены в C ++ 11. См. Документ, связанный с выше или этой статьей для деталей.
источник
Один пример - во встроенной области, где каждый бит регистра может означать что-то другое. Например, объединение 8-разрядного целого числа и структуры с 8 отдельными 1-разрядными битовыми полями позволяет изменять один бит или весь байт.
источник
void*
/ или масок и сдвигов.REG |= MASK
иREG &= ~MASK
. Если это подвержено ошибкам, поместите их в#define SETBITS(reg, mask)
и#define CLRBITS(reg, mask)
. Не полагайтесь на компилятор, чтобы получить биты в определенном порядке ( stackoverflow.com/questions/1490092/… )Херб Саттер написал в GOTW около шести лет назад, с акцентом добавил:
И для менее полезного примера посмотрите длинный, но неокончательный вопрос gcc, строгое псевдонимы и приведение через объединение .
источник
Ну, один пример использования, который я могу придумать, таков:
Затем вы можете получить доступ к 8-битным отдельным частям этого 32-битного блока данных; Тем не менее, подготовьтесь к тому, чтобы быть укушенным порядком байтов.
Это только один гипотетический пример, но всякий раз, когда вы хотите разделить данные в поле на составные части, как это, вы можете использовать объединение.
Тем не менее, есть также метод, который является endian-safe:
Например, поскольку эта двоичная операция будет преобразована компилятором в правильную последовательность.
источник
Некоторые использования для союзов:
Экономия места на диске, когда поля зависят от определенных значений:
Grep включаемые файлы для использования с вашим компилятором. Вы найдете десятки к сотням использования
union
:источник
Объединения полезны при работе с данными уровня байтов (низкого уровня).
Одним из моих недавних использований было моделирование IP-адресов, которое выглядит следующим образом:
источник
Пример, когда я использовал союз:
это позволяет мне получить доступ к моим данным в виде массива или элементов.
Я использовал объединение, чтобы разные термины указывали на одно и то же значение. При обработке изображений, работаю ли я над столбцами, шириной или размером в направлении X, это может сбить с толку. Чтобы решить эту проблему, я использую объединение, чтобы знать, какие описания идут вместе.
источник
Союзы обеспечивают полиморфизм в C.
источник
void*
сделал это ^^Блестящее использование объединения - выравнивание памяти, которое я нашел в исходном коде PCL (Point Cloud Library). Одна структура данных в API может быть ориентирована на две архитектуры: ЦП с поддержкой SSE, а также ЦП без поддержки SSE. Например, структура данных для PointXYZ
3 поплавка дополняются дополнительным поплавком для выравнивания SSE. Таким образом, для
Пользователь может получить доступ к point.data [0] или point.x (в зависимости от поддержки SSE) для доступа, скажем, к координате x. Больше похожих подробностей об использовании можно найти по следующей ссылке: Документация PCL Типы PointT
источник
union
Ключевое слово, в то время как до сих пор используется в C ++ 03 1 , в основном остаток дней C. Наиболее вопиющая проблема заключается в том, что он работает только с POD 1 .Идея объединения, однако, все еще присутствует, и библиотеки Boost действительно имеют класс, подобный объединению:
Который имеет большинство преимуществ
union
(если не все) и добавляет:На практике было продемонстрировано, что оно было эквивалентно комбинации
union
+enum
, и было отмечено, что оно было таким же быстрым (хотяboost::any
это больше относится к областиdynamic_cast
, поскольку оно использует RTTI).1 Союзы были обновлены в C ++ 11 ( неограниченные союзы ) и теперь могут содержать объекты с деструкторами, хотя пользователь должен вызывать деструктор вручную (на текущем активном члене объединения). Все еще намного проще использовать варианты.
источник
boost::variant
чем пытаться использовать союзы самостоятельно. Слишком много неопределенного поведения вокруг профсоюзов, что ваши шансы сделать все правильно, безнадежно.Из статьи Википедии о профсоюзах :
источник
В первые дни существования C (например, как было задокументировано в 1974 г.) все структуры имели общее пространство имен для своих членов. Каждое имя члена было связано с типом и смещением; если бы "wd_woozle" был "int" со смещением 12, то данный указатель
p
любого типа структурыp->wd_woozle
был бы эквивалентен*(int*)(((char*)p)+12)
. Язык требовал, чтобы все члены всех типов структур имели уникальные имена, за исключением того, что он явно разрешал повторное использование имен членов в тех случаях, когда каждая структура, в которой они использовались, воспринимала их как общую начальную последовательность.Тот факт, что типы структур могут использоваться беспорядочно, позволил им вести себя так, как будто они содержат перекрывающиеся поля. Например, даны определения:
Код может объявить структуру типа «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 эпохи интерпретации правил наложения спектров.источник
Допустим, у вас есть n различных типов конфигураций (просто набор переменных, определяющих параметры). Используя перечисление типов конфигурации, вы можете определить структуру, которая имеет идентификатор типа конфигурации, а также объединение всех различных типов конфигураций.
Таким образом, при передаче конфигурации можно использовать идентификатор, чтобы определить, как интерпретировать данные конфигурации, но если бы конфигурации были огромными, у вас не было бы необходимости иметь параллельные структуры для каждого пространства потери потенциального типа.
источник
Одно недавнее повышение уже объединенной важности объединения было дано Строгим правилом псевдонимов, введенным в недавней версии стандарта C.
Вы можете использовать союзы, чтобы печатать, не нарушая стандарт Си.
Эта программа имеет неопределенное поведение (потому что я предположил, что
float
иunsigned int
имеет ту же длину), но не неопределенное поведение (см. Здесь ).источник
Я хотел бы добавить один хороший практический пример для использования объединения - реализации калькулятора / интерпретатора формул или использования какого-то его вида в вычислениях (например, вы хотите использовать модифицируемые во время выполнения части ваших вычислительных формул - численное решение уравнений - просто например). Поэтому вы можете определить числа / константы разных типов (целые, с плавающей точкой и даже комплексные числа) следующим образом:
Таким образом, вы экономите память и, что более важно, вы избегаете любых динамических распределений для, возможно, экстремального количества (если вы используете много определенных во время выполнения чисел) небольших объектов (по сравнению с реализациями через наследование / полиморфизм классов). Но что более интересно, вы все еще можете использовать мощь полиморфизма C ++ (например, если вы фанат двойной диспетчеризации;) с этим типом структуры. Просто добавьте указатель «фиктивного» интерфейса на родительский класс всех типов чисел в качестве поля этой структуры, указывая на этот экземпляр вместо / в дополнение к необработанному типу, или используйте старые добрые указатели на функции Си.
так что вы можете использовать полиморфизм вместо проверки типов с помощью switch (type) - с реализацией с эффективным использованием памяти (без динамического выделения небольших объектов) - если вам это нужно, конечно.
источник
С http://cplus.about.com/od/learningc/ss/lowlevel_9.htm :
источник
Союзы предоставляют способ манипулировать различными видами данных в одной области хранения без встраивания в программу какой-либо машинно-независимой информации. Они аналогичны вариантным записям на паскале.
В качестве примера, который может быть найден в менеджере таблицы символов компилятора, предположим, что константа может быть int, float или символьным указателем. Значение конкретной константы должно храниться в переменной соответствующего типа, однако для управления таблицами наиболее удобно, если значение занимает одинаковый объем памяти и хранится в одном и том же месте независимо от его типа. Это цель объединения - единственная переменная, которая может на законных основаниях содержать любой из нескольких типов. Синтаксис основан на структурах:
Переменная u будет достаточно большой, чтобы вместить самый большой из трех типов; конкретный размер зависит от реализации. Любой из этих типов может быть назначен вам и затем использован в выражениях, если использование является последовательным
источник