union
Примечание.
В C++17 и более поздних версиях std::variant
class это типобезопасная альтернатива для union.
Это union
определяемый пользователем тип, в котором все члены совместно используют одно расположение памяти. Это определение означает, union что в любое время может содержать не более одного объекта из списка элементов. Это также означает, что независимо от количества элементов union всегда используется достаточно памяти для хранения самого большого элемента.
Это union может быть полезно для экономии памяти при наличии большого количества объектов и ограниченной памяти. Тем не менее, union требуется дополнительная помощь, чтобы правильно использовать. Вы несете ответственность за обеспечение постоянного доступа к одному и тому же участнику, которому вы назначены. Если какие-либо типы элементов имеют нетривиальный конstructили, необходимо написать код, чтобы явным образом иstruct уничтожить этот элемент. Прежде чем использовать union, рассмотрите ли проблему, которую вы пытаетесь решить, можно лучше выразить с помощью базовых class и производных class типов.
Синтаксис
union
tag
необ.{
member-list
};
Параметры
tag
Имя типа, заданное параметру union.
member-list
Элементы, которые union могут содержаться.
Объявление union
Начните объявление с union помощью union
ключевое слово и заключите список членов в фигурные скобки:
// declaring_a_union.cpp
union RecordType // Declare a simple union type
{
char ch;
int i;
long l;
float f;
double d;
int *int_ptr;
};
int main()
{
RecordType t;
t.i = 5; // t holds an int
t.f = 7.25; // t now holds a float
}
Использование union
В предыдущем примере любой код, обращаюющийся к union данным, должен знать, какой член содержит данные. Наиболее распространенное решение этой проблемы называется дискриминированным union. Он включает union в себя элементstruct, указывающий enum тип элемента, хранящийся в данный момент.union В следующем примере демонстрируется использование основного подхода:
#include <queue>
using namespace std;
enum class WeatherDataType
{
Temperature, Wind
};
struct TempData
{
int StationId;
time_t time;
double current;
double max;
double min;
};
struct WindData
{
int StationId;
time_t time;
int speed;
short direction;
};
struct Input
{
WeatherDataType type;
union
{
TempData temp;
WindData wind;
};
};
// Functions that are specific to data types
void Process_Temp(TempData t) {}
void Process_Wind(WindData w) {}
void Initialize(std::queue<Input>& inputs)
{
Input first;
first.type = WeatherDataType::Temperature;
first.temp = { 101, 1418855664, 91.8, 108.5, 67.2 };
inputs.push(first);
Input second;
second.type = WeatherDataType::Wind;
second.wind = { 204, 1418859354, 14, 27 };
inputs.push(second);
}
int main(int argc, char* argv[])
{
// Container for all the data records
queue<Input> inputs;
Initialize(inputs);
while (!inputs.empty())
{
Input const i = inputs.front();
switch (i.type)
{
case WeatherDataType::Temperature:
Process_Temp(i.temp);
break;
case WeatherDataType::Wind:
Process_Wind(i.wind);
break;
default:
break;
}
inputs.pop();
}
return 0;
}
В предыдущем примере union имяstructInput
отсутствует, поэтому он называется анонимнымunion. Доступ к его членам можно получить напрямую, как если бы они являются членами .struct Дополнительные сведения об использовании анонимного unionсм. в разделе "Анонимныйunion".
В предыдущем примере показана проблема, которую также можно решить с помощью class типов, производных от общей базы class. Вы можете ветвить код на основе типа среды выполнения каждого объекта в контейнере. Ваш код может быть проще поддерживать и понимать, но он также может быть медленнее, чем использование union. Кроме того, с unionпомощью приложения можно хранить несвязанные типы. Позволяет union динамически изменять тип хранимого значения, не изменяя тип самой переменной union . Например, можно создать разнородный массив MyUnionType
, элементы которого хранят разные значения разных типов.
Это легко неправильно использовать Input
struct в примере. Это до пользователя, чтобы использовать дискриминатор правильно для доступа к члену, в котором хранятся данные. Вы можете защититься от неправильного unionprivate
использования, сделав и предоставив специальные функции доступа, как показано в следующем примере.
Неограниченный union (C++11)
В C++03 и более ранних версиях union может содержатьstatic не члены данных, имеющие class тип, если у типа нет предоставленных пользователем constructors, destructors или операторов назначения. В C++11 эти ограничения отсутствуют. Если в него включен такой членunion, компилятор автоматически помечает какие-либо специальные функции-члены, которые не предоставляются пользователем.deleted
union Если он является анонимным union внутри class или struct, то любые специальные функции-члены classstruct или не предоставленные пользователем помечены как deleted
. В следующем примере показано, как обрабатывать этот случай. Один из членов имеет член, который требует этого специального union лечения:
// for MyVariant
#include <crtdbg.h>
#include <new>
#include <utility>
// for sample objects and output
#include <string>
#include <vector>
#include <iostream>
using namespace std;
struct A
{
A() = default;
A(int i, const string& str) : num(i), name(str) {}
int num;
string name;
//...
};
struct B
{
B() = default;
B(int i, const string& str) : num(i), name(str) {}
int num;
string name;
vector<int> vec;
// ...
};
enum class Kind { None, A, B, Integer };
#pragma warning (push)
#pragma warning(disable:4624)
class MyVariant
{
public:
MyVariant()
: kind_(Kind::None)
{
}
MyVariant(Kind kind)
: kind_(kind)
{
switch (kind_)
{
case Kind::None:
break;
case Kind::A:
new (&a_) A();
break;
case Kind::B:
new (&b_) B();
break;
case Kind::Integer:
i_ = 0;
break;
default:
_ASSERT(false);
break;
}
}
~MyVariant()
{
switch (kind_)
{
case Kind::None:
break;
case Kind::A:
a_.~A();
break;
case Kind::B:
b_.~B();
break;
case Kind::Integer:
break;
default:
_ASSERT(false);
break;
}
kind_ = Kind::None;
}
MyVariant(const MyVariant& other)
: kind_(other.kind_)
{
switch (kind_)
{
case Kind::None:
break;
case Kind::A:
new (&a_) A(other.a_);
break;
case Kind::B:
new (&b_) B(other.b_);
break;
case Kind::Integer:
i_ = other.i_;
break;
default:
_ASSERT(false);
break;
}
}
MyVariant(MyVariant&& other)
: kind_(other.kind_)
{
switch (kind_)
{
case Kind::None:
break;
case Kind::A:
new (&a_) A(move(other.a_));
break;
case Kind::B:
new (&b_) B(move(other.b_));
break;
case Kind::Integer:
i_ = other.i_;
break;
default:
_ASSERT(false);
break;
}
other.kind_ = Kind::None;
}
MyVariant& operator=(const MyVariant& other)
{
if (&other != this)
{
switch (other.kind_)
{
case Kind::None:
this->~MyVariant();
break;
case Kind::A:
*this = other.a_;
break;
case Kind::B:
*this = other.b_;
break;
case Kind::Integer:
*this = other.i_;
break;
default:
_ASSERT(false);
break;
}
}
return *this;
}
MyVariant& operator=(MyVariant&& other)
{
_ASSERT(this != &other);
switch (other.kind_)
{
case Kind::None:
this->~MyVariant();
break;
case Kind::A:
*this = move(other.a_);
break;
case Kind::B:
*this = move(other.b_);
break;
case Kind::Integer:
*this = other.i_;
break;
default:
_ASSERT(false);
break;
}
other.kind_ = Kind::None;
return *this;
}
MyVariant(const A& a)
: kind_(Kind::A), a_(a)
{
}
MyVariant(A&& a)
: kind_(Kind::A), a_(move(a))
{
}
MyVariant& operator=(const A& a)
{
if (kind_ != Kind::A)
{
this->~MyVariant();
new (this) MyVariant(a);
}
else
{
a_ = a;
}
return *this;
}
MyVariant& operator=(A&& a)
{
if (kind_ != Kind::A)
{
this->~MyVariant();
new (this) MyVariant(move(a));
}
else
{
a_ = move(a);
}
return *this;
}
MyVariant(const B& b)
: kind_(Kind::B), b_(b)
{
}
MyVariant(B&& b)
: kind_(Kind::B), b_(move(b))
{
}
MyVariant& operator=(const B& b)
{
if (kind_ != Kind::B)
{
this->~MyVariant();
new (this) MyVariant(b);
}
else
{
b_ = b;
}
return *this;
}
MyVariant& operator=(B&& b)
{
if (kind_ != Kind::B)
{
this->~MyVariant();
new (this) MyVariant(move(b));
}
else
{
b_ = move(b);
}
return *this;
}
MyVariant(int i)
: kind_(Kind::Integer), i_(i)
{
}
MyVariant& operator=(int i)
{
if (kind_ != Kind::Integer)
{
this->~MyVariant();
new (this) MyVariant(i);
}
else
{
i_ = i;
}
return *this;
}
Kind GetKind() const
{
return kind_;
}
A& GetA()
{
_ASSERT(kind_ == Kind::A);
return a_;
}
const A& GetA() const
{
_ASSERT(kind_ == Kind::A);
return a_;
}
B& GetB()
{
_ASSERT(kind_ == Kind::B);
return b_;
}
const B& GetB() const
{
_ASSERT(kind_ == Kind::B);
return b_;
}
int& GetInteger()
{
_ASSERT(kind_ == Kind::Integer);
return i_;
}
const int& GetInteger() const
{
_ASSERT(kind_ == Kind::Integer);
return i_;
}
private:
Kind kind_;
union
{
A a_;
B b_;
int i_;
};
};
#pragma warning (pop)
int main()
{
A a(1, "Hello from A");
B b(2, "Hello from B");
MyVariant mv_1 = a;
cout << "mv_1 = a: " << mv_1.GetA().name << endl;
mv_1 = b;
cout << "mv_1 = b: " << mv_1.GetB().name << endl;
mv_1 = A(3, "hello again from A");
cout << R"aaa(mv_1 = A(3, "hello again from A"): )aaa" << mv_1.GetA().name << endl;
mv_1 = 42;
cout << "mv_1 = 42: " << mv_1.GetInteger() << endl;
b.vec = { 10,20,30,40,50 };
mv_1 = move(b);
cout << "After move, mv_1 = b: vec.size = " << mv_1.GetB().vec.size() << endl;
cout << endl << "Press a letter" << endl;
char c;
cin >> c;
}
Не union удается сохранить ссылку. Кроме того, не union поддерживается наследование. Это означает, что вы не можете использовать union как базу classили наследовать от другого classили иметь виртуальные функции.
Инициализация union
Можно объявить и инициализировать union в той же инструкции, назначив выражение, заключенное в фигурные скобки. Выражение вычисляется и назначается первому полю union.
#include <iostream>
using namespace std;
union NumericType
{
short iValue;
long lValue;
double dValue;
};
int main()
{
union NumericType Values = { 10 }; // iValue = 10
cout << Values.iValue << endl;
Values.dValue = 3.1416;
cout << Values.dValue << endl;
}
/* Output:
10
3.141600
*/
Он NumericType
union упорядочен в памяти (концептуально), как показано на следующем рисунке:
На схеме показаны 8 байт данных. Двойной тип dValue занимает весь 8 байт. Тип long lValue занимает первые 4 байта. Короткий тип iValue занимает первый байт.
Анонимные union
Анонимный union объявлен без class-name
или declarator-list
.
union {
member-list
}
Имена, объявленные в анонимном, union используются напрямую, например переменные nonmember. Это означает, что имена, объявленные в анонимном, union должны быть уникальными в окружающем область.
Анонимный union имеет следующие ограничения:
- Если он объявлен в файле или пространстве имен область, он также должен быть объявлен как
static
. - У него могут быть только
public
члены; наличиеprivate
иprotected
члены в анонимном union генерации ошибок. - Он не может иметь функции-члены.
См. также
Обратная связь
https://aka.ms/ContentUserFeedback.
Ожидается в ближайшее время: в течение 2024 года мы постепенно откажемся от GitHub Issues как механизма обратной связи для контента и заменим его новой системой обратной связи. Дополнительные сведения см. в разделеОтправить и просмотреть отзыв по