union
Poznámka:
V jazyce C++17 a novějším std::variant
class se jedná o typově bezpečnou alternativu pro .union
A union
je uživatelem definovaný typ, ve kterém všichni členové sdílejí stejné umístění paměti. Tato definice znamená, že v daném okamžiku union nesmí obsahovat více než jeden objekt ze seznamu členů. Také to znamená, že bez ohledu na to, kolik členů union má, vždy používá pouze dostatek paměti k uložení největšího člena.
Může union být užitečné pro úsporu paměti, pokud máte velké množství objektů a omezenou paměť. Vyžaduje však union větší péči, aby se správně používala. Zodpovídáte za to, že budete mít vždy přístup ke stejnému členu, kterému jste přiřadili. Pokud některé typy členů mají nontriviální konstruktor, musíte napsat kód, který explicitně sestaví a zničí tento člen. Než začnete používat union, zvažte, jestli by problém, který se pokoušíte vyřešit, lépe vyjádřit pomocí základního class a odvozeného class typu.
Syntaxe
union
tag
volit{
member-list
};
Parametry
tag
Názevtypuho union
member-list
Členové, které union můžou obsahovat.
Deklarace union
Zahajte deklaraci klíčového union union
slova a uzavřete seznam členů do složených závorek:
// 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
}
Použití union
V předchozím příkladu musí každý kód, který přistupuje k union datům, vědět, který člen obsahuje data. Nejběžnějším řešením tohoto problému je diskriminováno union. Uzavře do objektu union structa obsahuje enum člen, který označuje typ členu, který je aktuálně uložen v objektu union. Následující příklad ukazuje základní vzor:
#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;
}
V předchozím příkladu union Input
struct neobsahuje žádné jméno, takže se nazývá anonymní.union K jeho členům lze přistupovat přímo, jako by byli členy struct. Další informace o použití anonymního unionsouboru naleznete v části Anonymní union .
Předchozí příklad ukazuje problém, který byste mohli vyřešit také pomocí class typů odvozených od společného základu class. Kód můžete rozvětvovat na základě typu modulu runtime každého objektu v kontejneru. Váš kód může být snazší udržovat a pochopit, ale může být také pomalejší než použití union. unionMůžete také ukládat nesouvisející typy. Funkce A union umožňuje dynamicky měnit typ uložené hodnoty beze změny typu union samotné proměnné. Můžete například vytvořit heterogenní pole MyUnionType
, jehož prvky ukládají různé hodnoty různých typů.
V příkladu je snadné zneužít Input
struct . Je na uživateli, aby pro přístup k členovi, který obsahuje data, správně používal diskriminátor. Můžete chránit před zneužitím tím, že vytvoříte unionprivate
a poskytnete speciální přístupové funkce, jak je znázorněno v dalším příkladu.
union Neomezené (C++11)
V jazyce C++03 a starším union může obsahovat nestatické datové členy, které mají typ class , pokud typ neobsahuje žádné konstruktory, destruktory nebo operátory přiřazení poskytnuté uživatelem. V jazyce C++11 jsou tato omezení odebrána. Pokud takový člen zahrnete do svého union, kompilátor automaticky označí všechny speciální členské funkce, které nejsou poskytovány jako deleted
. Pokud je anonymní uvnitř nebo structclass , jsou všechny speciální členské funkce struct class poskytnuté uživatelem označeny jako deleted
.union union Následující příklad ukazuje, jak tento případ zpracovat. Jeden ze členů union má člena, který vyžaduje toto zvláštní zacházení:
// 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;
}
Odkaz union nelze uložit. A union také nepodporuje dědičnost. To znamená, že nemůžete použít union jako základ classnebo dědit z jiného classnebo mít virtuální funkce.
Inicializace union
Můžete deklarovat a inicializovat union ve stejném příkazu přiřazením výrazu uzavřeného do složených závorek. Výraz se vyhodnotí a přiřadí prvnímu unionpoli .
#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
*/
Je NumericType
union uspořádaný v paměti (koncepčně), jak je znázorněno na následujícím obrázku:
Diagram znázorňuje 8 bajtů dat. Dvojitý typ dValue zabírá celých 8 bajtů. Typ long lValue zabírá prvních 4 bajtů. Krátký typ iValue zabírá první bajt.
Anonymní union
Anonymní union je jeden deklarovaný bez nebo class-name
declarator-list
.
union {
member-list
}
Názvy deklarované v anonymním union objektu se používají přímo, například nečlenné proměnné. Z toho vyplývá, že názvy deklarované v anonymním union objektu musí být v okolním oboru jedinečné.
union Anonymní se řídí těmito omezeními:
- Pokud je deklarován v oboru názvů nebo souboru, musí být deklarován také jako
static
. - Může obsahovat pouze
public
členy, kteří majíprivate
aprotected
členové v anonymním objektu union generují chyby. - Nemůže mít členské funkce.