Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Observação
No C++17 e posterior, é std::variantclass uma alternativa de tipo seguro para um union.
Um union é um tipo definido pelo usuário no qual todos os membros compartilham o mesmo local de memória. Essa definição significa que, a qualquer momento, um union não pode conter mais de um objeto de sua lista de membros. Isso também significa que, não importa quantos membros union tenha, ele sempre usa memória suficiente para armazenar o maior membro.
Pode union ser útil para conservar memória quando você tem muitos objetos e memória limitada. No entanto, é union necessário um cuidado extra para usar corretamente. Você é responsável por garantir que sempre acesse o mesmo membro atribuído. Se algum tipo de membro tiver um construtor nãotrivial, você deverá escrever código para construir e destruir explicitamente esse membro explicitamente. Antes de usar um union, considere se o problema que você está tentando resolver pode ser melhor expresso usando uma base class e tipos derivados class .
Sintaxe
uniontagoptar{member-list};
Parâmetros
tag
O nome do tipo fornecido ao union.
member-list
Membros que podem union conter.
Declarar um union
Inicie a declaração de um union usando a union palavra-chave e coloque a lista de membros em chaves:
// 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
}
Usar um union
No exemplo anterior, qualquer código que acesse as union necessidades de saber qual membro contém os dados. A solução mais comum para esse problema é chamada de discriminado union. Ele inclui um unionstructenum membro que indica o tipo de membro atualmente armazenado no .union O exemplo a seguir mostra o padrão básico:
#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;
}
No exemplo anterior, o union in no Inputstruct não tem nome, portanto, ele é chamado de anônimounion. Seus membros podem ser acessados diretamente como se fossem membros do struct. Para obter mais informações sobre como usar um anônimo union, consulte a seção Anônimo union .
O exemplo anterior mostra um problema que você também pode resolver usando class tipos que derivam de uma base classcomum. Você pode ramificar seu código com base no tipo de runtime de cada objeto no contêiner. Seu código pode ser mais fácil de manter e entender, mas também pode ser mais lento do que usar um union. Além disso, com um union, você pode armazenar tipos não relacionados. Permite union alterar dinamicamente o tipo do valor armazenado sem alterar o tipo da union variável em si. Por exemplo, você pode criar uma matriz heterogênea de MyUnionType, cujos elementos armazenam valores diferentes de tipos diferentes.
É fácil usar indevidamente o Inputstruct exemplo. Cabe ao usuário usar o discriminador corretamente para acessar o membro que contém os dados. Você pode proteger contra uso indevido, tornando as unionprivate funções de acesso especiais e fornecidas, conforme mostrado no próximo exemplo.
Irrestrito union (C++11)
No C++03 e anterior, um union pode conter membros de dados não estáticos que têm um class tipo, desde que o tipo não tenha construtores, destruidores ou operadores de atribuição fornecidos pelo usuário. No C++11, essas restrições são removidas. Se você incluir um membro desse tipo em seu unioncompilador, o compilador marcará automaticamente as funções de membro especiais que não são fornecidas pelo usuário como deleted. Se for union um anônimo union dentro de um class ou struct, as funções de membro especiais do class usuário ou struct que não forem fornecidas pelo usuário serão marcadas como deleted. O exemplo a seguir mostra como lidar com esse caso. Um dos membros do union membro tem um membro que requer este tratamento especial:
// 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;
}
Não union é possível armazenar uma referência. Um union também não dá suporte à herança. Isso significa que você não pode usar uma union base classou herdar de outra classou ter funções virtuais.
Inicializar um union
Você pode declarar e inicializar uma union na mesma instrução atribuindo uma expressão entre chaves. A expressão é avaliada e atribuída ao primeiro campo do 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
*/
O NumericTypeunion é organizado na memória (conceitualmente), conforme mostrado na figura a seguir:
O diagrama mostra 8 bytes de dados. O dValue de tipo duplo ocupa os 8 bytes inteiros. O tipo lValue longo ocupa os primeiros 4 bytes. O tipo curto iValue ocupa o primeiro byte.
Anônimo union
Um anônimo union é declarado sem um class-name ou declarator-list.
union {member-list}
Os nomes declarados em um anônimo union são usados diretamente, como variáveis não membros. Isso implica que os nomes declarados em um anônimo union devem ser exclusivos no escopo ao redor.
Um anônimo union está sujeito a estas restrições:
- Se declarado no escopo de arquivo ou namespace, ele também deve ser declarado como
static. - Ele pode ter apenas
publicmembros; terprivateeprotectedmembros em um anônimo union gera erros. - Ele não pode ter funções de membro.