Объединения в С++ (union C++)
Технология объединений union берет свои истоки в 90-х. Слабенькие по нашим временам ЭВМ (сейчас их и компьютерами то не назовешь), мало памяти (все измерялось килобайтами). Жесткие диски по 40 мегабайт были чуть ли не чудом техники, олицетворяющим огроменные объемы информации, которые умельцами “растачивались” специальными ПО до хранения 80 мегабайт (чтоб побольше было).
Каждый байт памяти был на вес золота, приходилось экономить на всём. Вот и объединение переменных тоже было призвано (и достаточно успешно, о чем я расскажу ниже) помочь программисту сделать оптимальную и экономную программу, “кушающую” маленькие объемы памяти.
Чтобы понять в чем смысл объединения нужно вспомнить как хранятся переменные. Разные переменные разного типа или одинаковой группы типов (вроде int , long и short ) несмотря на работу с одним и тем же типом данных (имею ввиду целое) занимают в памяти разное количество байт. long в любом случае занимает максимальное количество байт в памяти, при этом в память для переменной этого типа вполне можно записать значения int или short .
Просто получится, что не все зарезервированные байты long -а будут востребованы. Если поместить к примеру число 325 в long , будут заняты два байта (зависит от разрядности процессора), а остальные байты заполнятся нулями.
Именно в этом и появляется смысл union , ибо эта инструкция говорит компилятору: “Зарезервируй мне место для типа данных с максимальным запросом объема памяти, а я уже буду сама разбираться, как и какие значения в них положить”.
Объединение – это такой формат данных, который подобен структуре. Оно (объединение) способно хранить в пределах одной зарезервированной области памяти различные типы данных. Но в каждый определенный момент времени в объединении хранится только один из этих типов данных и возможно использовать лишь значение этого элемента (компонента). Синтаксически определяют объединение так (очень похоже на определение структуры):
Доступ к элементам объединения осуществляется так же, как и к элементам структур: Имя объекта объединения myUnion , точка . и имя элемента name1 .
К данным, которые хранят элементы структуры (например short, int, long ) мы можем обращаться в любой момент (хоть к одному, хоть и ко всем сразу). А в объединении могут храниться данные либо short , либо int , либо long . Например:

Как видите, после того, как мы записали значение в элемент name3 типа long int , уже невозможно нормально обращаться к элементу name1 . Все потому, что в их общую память уже записано значение long int , а переменная типа short int неспособна работать с данными такого объема. Схематически можно это представить так:

Поэтому, чтобы опять работать с данными типа short int необходимо снова присвоить элементу name1 новое значение. Вот и получается – память одна и та же, а переменные в ней размещаются разные. К какой обращаемся – такая и запрашивает из этой памяти значение.
Элементы объединения располагаются в памяти начиная с одного места (как бы накладываются друг на друга), в отличии от элементов структуры (они располагаются в памяти последовательно один за другим).
Применение объединений вызвано необходимостью экономии памяти, когда нужно хранить и использовать данные разных типов, но обращаться к ним можно не одновременно.
Если бы мы описали просто набор переменных не объединяя их в union , то для их размещения потребовалось бы 2 + 4 + 4 байта = 10 байт. Вот и экономия. А так объединение занимает 4 байта. Целых 6 байт сэкономили, натрамбовав три переменные в один отрезок памяти.
К слову нужно заметить, что эта технология умудрилась получить неплохое продолжение, и в современных языках используется везде где нужно и не нужно. СиШарп, PHP, Делфи, современные наследники Си плюс плюс – все они используют такие объединения. Только в современном мире они уже называются variant .
В переменную variant можно без проблем запихнуть до 10 байт (10 это максимум для 32-х разрядных систем под вещественное число). Соответственно в эти 10 байт и все семейство целых и вещественных, и указатели, и объекты, и строки (точнее описатели строк или указатели на них) вмещаются.
На сегодняшний день экономия памяти не так актуальна, как 15-20 лет назад. Конечно, никто не запрещает использовать union , но зачем? Если есть более удобные на сегодня средства работы с памятью для программиста.
Следует отметить также, что программы для выполнения в память загружаются с избытком (т.е. для программы выделяется количество памяти, зачастую чрезвычайно большее чем надо). Это можно наблюдать, написав программу с одной переменной. Казалось бы – переменная 4 байта, но открыв диспетчер задач в Винде программа “почему-то” заняла 10 килобайт. Так работает современная операционная система, выделяя памяти с лихвой. В 90-х, увы, такой роскоши и быть не могло.
В следующем уроке мы рассмотрим битовые поля в С++. Не поленитесь посмотреть видео по теме Объединения (union) в С++:
Операции Distinct, Union, Except и Intersect

Операции множеств используются для выполнения математических операций с множествами на последовательностях.
Distinct
Операция Distinct удаляет дублированные элементы из входной последовательности. У операции Distinct есть один прототип, описанный ниже:
Эта операция возвращает объект, перечисляющий элементы входной последовательности source и выдающий последовательность, в которой каждый элемент не эквивалентен предыдущим выданным. Эквивалентность элементов определяется методами GetHashCode и Equals.
Давайте рассмотрим пример использования операции Distinct:

Union
Операция Union возвращает объединение множеств из двух исходных последовательностей. У этой операции имеется один прототип, описанный ниже:
Эта операция возвращает объект, который сначала перечисляет элементы последовательности по имени first, выдавая последовательность, в которой каждый элемент не эквивалентен предыдущим выданным, затем перечисляет вторую входную последовательность second, опять-таки, выдавая последовательность без повторений. Эквивалентность элементов определяется методами GetHashCode и Equals.
Чтобы продемонстрировать разницу между операцией Union и описанной ранее операцией Concat, в примере, представленном ниже, создаются последовательности first и second из массива cars, что приведет к дублированию пятого элемента в обеих последовательностях. Затем отображается количество элементов в массиве cars, а также в последовательностях first и second, наряду с количеством элементов в конкатенированной и объединенной последовательностях:
В конечном итоге последовательность concat должна иметь на один элемент больше, чем массив cars. Последовательность union должна содержать то же количество элементов, что и массив cars. Это доказывают результаты выполнения кода:

Intersect
Операция Intersect возвращает пересечение множеств из двух исходных последовательностей. Операция Intersect имеет один прототип, описанный ниже:
Эта операция возвращает объект, который сначала перечисляет элементы последовательности по имени first, выбирая оттуда каждый элемент, который не эквивалентен предыдущему выбранному элементу. Затем он перечисляет вторую входную последовательность, помечая любой элемент, имеющийся в обеих последовательностях, для включения в выходную последовательность. Затем осуществляется проход по помеченным элементам, с помещением их в выходную последовательность в том порядке, в котором они были собраны. Эквивалентность элементов определяется с помощью методов GetHashCode и Equals.
Чтобы продемонстрировать применение операции Intersect, ниже используются операции Take и Skip для генерации двух последовательностей и получения некоторого их перекрытия, как в примере с Union, где был намеренно дублирован пятый элемент. После вызова операции Intersect на этих двух сгенерированных последовательностях в возвращаемой последовательности intersect должен оказаться только дублированный пятый элемент:
В конечном итоге должна быть получена последовательность auto, которая состоит только из одного элемента, содержащего дублированный пятый элемент массива cars — Chevrolet:

Итак, LINQ работает! Насколько часто вам приходилось ранее выполнять операции над множествами элементов из двух коллекций? Было ли это трудно? Благодаря LINQ, все сложности в прошлом.
Except
Операция Except возвращает последовательность, содержащую все элементы первой последовательности, которых нет во второй последовательности. Эта операция имеет один прототип, описанный ниже:
Эта операция возвращает объект, который при перечислении перебирает элементы входной последовательности по имени second, собирая все элементы, которые не эквивалентны ранее собранным. Затем происходит перечисление входной последовательности first, с выдачей каждого ее элемента, которого нет в коллекции из второй последовательности. Эквивалентность одного элемента другому определяется с использованием их методов GetHashCode и Equals.
В следующем примере используются два массива arr1 и arr2. С помощью операции Except будут удалены взаимоисключающие элементы коллекций:
Union declaration
A union is a special class type that can hold only one of its non-static data members at a time.
The class specifier for a union declaration is similar to class or struct declaration:
| union attr class-head-name <member-specification > |
| attr | — | (since C++11) optional sequence of any number of attributes |
| class-head-name | — | the name of the union that’s being defined. Optionally prepended by nested-name-specifier (sequence of names and scope-resolution operators, ending with scope-resolution operator). The name may be omitted, in which case the union is unnamed |
| member-specification | — | list of access specifiers, member object and member function declarations and definitions. |
A union can have member functions (including constructors and destructors), but not virtual functions.
A union cannot have base classes and cannot be used as a base class.
A union cannot have non-static data members of reference types.
Unions cannot contain a non-static data member with a non-trivial special member function (copy constructor, copy-assignment operator, or destructor).
If a union contains a non-static data member with a non-trivial special member function (copy/move constructor, copy/move assignment, or destructor), that function is deleted by default in the union and needs to be defined explicitly by the programmer.
If a union contains a non-static data member with a non-trivial default constructor, the default constructor of the union is deleted by default unless a variant member of the union has a default member initializer .
Just like in struct declaration, the default member access in a union is public.
Contents
[edit] Explanation
The union is only as big as necessary to hold its largest data member. The other data members are allocated in the same bytes as part of that largest member. The details of that allocation are implementation-defined but all non-static data members will have the same address (since C++14) . It’s undefined behavior to read from the member of the union that wasn’t most recently written. Many compilers implement, as a non-standard language extension, the ability to read inactive members of a union.
Each member is allocated as if it is the only member of the class.
If members of a union are classes with user-defined constructors and destructors, to switch the active member, explicit destructor and placement new are generally needed:
If two union members are standard-layout types, it’s well-defined to examine their common subsequence on any compiler.
[edit] Member lifetime
The lifetime of a union member begins when the member is made active. If another member was active previously, its lifetime ends.
When active member of a union is switched by an assignment expression of the form E1 = E2 that uses either the built-in assignment operator or a trivial assignment operator, for each union member X that appears in the member access and array subscript subexpressions of E1 that is not a class with non-trivial or deleted default constructors, if modification of X would have undefined behavior under type aliasing rules, an object of the type of X is implicitly created in the nominated storage; no initialization is performed and the beginning of its lifetime is sequenced after the value computation of the left and right operands and before the assignment.
Trivial move constructor, move assignment operator, (since C++11) copy constructor and copy assignment operator of union types copy object representations. If the source and the destination are not the same object, these special member functions start lifetime of every object (except for objects that are neither subobjects of the destination nor of implicit-lifetime type) nested in the destination corresponding to the one nested in the source before the copy is performed. Otherwise, they do nothing. Two union objects have the same corresponding active member (if any) after construction or assignment via trivial special functions.
[edit] Anonymous unions
An anonymous union is an unnamed union definition that does not simultaneously define any variables (including objects of the union type, references, or pointers to the union).
| union <member-specification > ; |
Anonymous unions have further restrictions: they cannot have member functions, cannot have static data members, and all their data members must be public. The only declarations allowed are non-static data members and static_assert declarations (since C++11) .
Members of an anonymous union are injected in the enclosing scope (and must not conflict with other names declared there).
Namespace-scope anonymous unions must be declared static unless they appear in an unnamed namespace.
[edit] Union-like classes
A union-like class is either a union, or a (non-union) class that has at least one anonymous union as a member. A union-like class has a set of variant members: