Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Nota:
En C++17 y versiones posteriores, std::variantclass es una alternativa segura para tipos para .union
es union un tipo definido por el usuario en el que todos los miembros comparten la misma ubicación de memoria. Esta definición significa que, en un momento dado, un union objeto no puede contener más de un objeto de su lista de miembros. También significa que, independientemente del número de miembros union que tenga, siempre usa suficiente memoria para almacenar el miembro más grande.
Un union puede ser útil para conservar la memoria cuando tiene muchos objetos y memoria limitada. Sin embargo, un elemento union requiere un cuidado adicional para usarlo correctamente. Usted es responsable de asegurarse de que siempre tiene acceso al mismo miembro que asignó. Si algún tipo de miembro tiene un constructor notrivial, debe escribir código para construir y destruir explícitamente ese miembro. Antes de usar un union, considere si el problema que intenta resolver podría expresarse mejor mediante un tipo base class y derivado class .
Sintaxis
uniontagoptar{member-list};
Parámetros
tag
Nombre de tipo proporcionado a .union
member-list
Miembros que union puede contener.
Declarar un union
Comience la declaración de un union mediante la union palabra clave y incluya la lista de miembros entre llaves:
// 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 un union
En el ejemplo anterior, cualquier código que tenga acceso union a las necesidades de saber qué miembro contiene los datos. La solución más común a este problema se denomina discriminada union. Incluye en union un structelemento , e incluye un enum miembro que indica el tipo de miembro almacenado actualmente en .union En el ejemplo siguiente se muestra el patrón 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;
}
En el ejemplo anterior, en union no Inputstruct tiene nombre, por lo que se denomina anónimounion. Se puede acceder a sus miembros directamente como si fueran miembros de .struct Para obtener más información sobre cómo usar un anónimo union, consulte la sección Anónimo union .
En el ejemplo anterior se muestra un problema que también se podría resolver mediante el uso class de tipos que derivan de una base classcomún. Puede bifurcar el código en función del tipo en tiempo de ejecución de cada objeto del contenedor. Es posible que el código sea más fácil de mantener y comprender, pero también puede ser más lento que usar .union Además, con un union, puede almacenar tipos no relacionados. Un union permite cambiar dinámicamente el tipo del valor almacenado sin cambiar el tipo de la union propia variable. Por ejemplo, podría crear una matriz heterogénea de MyUnionType, cuyos elementos almacenan valores diferentes de distintos tipos.
Es fácil usar incorrectamente en Inputstruct el ejemplo. Es necesario que el usuario use el discriminador correctamente para acceder al miembro que contiene los datos. Para protegerse contra el uso indebido, realice unionprivate y proporcione funciones de acceso especiales, como se muestra en el ejemplo siguiente.
Sin restricciones union (C++11)
En C++03 y versiones anteriores, un union puede contener miembros de datos no estáticos que tienen un class tipo, siempre y cuando el tipo no tenga constructores, destructores o operadores de asignación proporcionados por el usuario. En C++11, se quitan estas restricciones. Si incluye este tipo de miembro en union, el compilador marca automáticamente las funciones miembro especiales que no se proporcionan por el usuario como deleted.
union Si es un anónimo union dentro de o classstruct, las funciones miembro especiales de class o struct que no se proporcionan por el usuario se marcan como deleted. En el ejemplo siguiente se muestra cómo controlar este caso. Uno de los miembros del union tiene un miembro que requiere este tratamiento 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;
}
No union se puede almacenar una referencia. Tampoco union admite la herencia. Esto significa que no se puede usar union como base classo heredar de otra classo tener funciones virtuales.
Inicialización de un union
Puede declarar e inicializar una union en la misma instrucción asignando una expresión entre llaves. La expresión se evalúa y se asigna al primer campo de .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 se organiza en memoria (conceptualmente) como se muestra en la ilustración siguiente:
El diagrama muestra 8 bytes de datos. El tipo doble dValue ocupa los 8 bytes completos. El tipo long lValue ocupa los primeros 4 bytes. El tipo corto iValue ocupa el primer byte.
Anónimo union
Un anónimo union es uno declarado sin o class-namedeclarator-list.
union {member-list}
Los nombres declarados en un anónimo union se usan directamente, como variables que no son miembros. Implica que los nombres declarados en un anónimo union deben ser únicos en el ámbito circundante.
Un anónimo union está sujeto a estas restricciones:
- Si se declara en el ámbito de archivo o espacio de nombres, también debe declararse como
static. - Solo
publicpuede tener miembros; tenerprivateyprotectedmiembros en un anónimo union genera errores. - No puede tener funciones miembro.