union
Hinweis
In C++17 und höher ist dies std::variant
class eine typsichere Alternative für eine union.
A union
ist ein benutzerdefinierter Typ, in dem alle Mitglieder denselben Speicherort gemeinsam nutzen. Diese Definition bedeutet, dass ein union Objekt zu einem bestimmten Zeitpunkt nicht mehr als ein Objekt aus der Liste der Member enthalten kann. Es bedeutet auch, dass unabhängig davon, wie viele Mitglieder vorhanden union sind, immer nur genügend Arbeitsspeicher zum Speichern des größten Mitglieds verwendet wird.
Eine union kann nützlich sein, um Arbeitsspeicher zu sparen, wenn Sie über viele Objekte und begrenzten Arbeitsspeicher verfügen. Eine union zusätzliche Sorgfalt ist jedoch erforderlich, um richtig zu verwenden. Sie sind dafür verantwortlich, sicherzustellen, dass Sie immer auf das von Ihnen zugewiesene Mitglied zugreifen. Wenn Membertypen über eine nichttrivielle Constructoder verfügen, müssen Sie Code schreiben, um dieses Element explizit zu konvertierenstruct und zu zerstören. Überlegen Sie vor der Verwendung eines unionProblems, ob das Problem, das Sie lösen möchten, besser ausgedrückt werden könnte, indem Sie eine Basis class und abgeleitete class Typen verwenden.
Syntax
union
tag
opt{
member-list
};
Parameter
tag
Der Name des Typs, der der union.
member-list
Elemente, die enthalten union können.
Deklarieren eines union
Beginnen Sie die Deklaration einer mithilfe union der union
Schlüsselwort (keyword), und schließen Sie die Memberliste in geschweifte Klammern ein:
// 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
}
Verwenden einer union
Im vorherigen Beispiel muss jeder Code, der auf die union Anforderungen zugreift, wissen, welches Element die Daten enthält. Die häufigste Lösung für dieses Problem wird als diskriminiert unionbezeichnet. Er schließt das union Element in ein structund enthält ein enum Element, das den elementtyp angibt, der derzeit in der union. Das grundlegende Muster wird im folgenden Beispiel veranschaulicht:
#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;
}
Im vorherigen Beispiel hat das union Inser Input
struct keinen Namen, daher wird er als anonymunion bezeichnet. Auf seine Mitglieder kann direkt zugegriffen werden, als ob sie Mitglieder der struct. Weitere Informationen zur Verwendung eines anonymen unionElements finden Sie im Abschnitt "Anonym" union .
Das vorherige Beispiel zeigt ein Problem, das Sie auch mithilfe class von Typen lösen können, die von einer gemeinsamen Basis classabgeleitet sind. Sie können den Code basierend auf dem Laufzeittyp jedes Objekts im Container verzweigen. Ihr Code ist möglicherweise einfacher zu Standard und zu verstehen, aber es kann auch langsamer sein als die Verwendung einer union. Mit einem union, können Sie auch nicht verknüpfte Typen speichern. Mithilfe von A union können Sie den Typ des gespeicherten Werts dynamisch ändern, ohne den Typ der union Variablen selbst zu ändern. Sie können z. B. ein heterogenes Array erstellen MyUnionType
, dessen Elemente unterschiedliche Werte unterschiedlicher Typen speichern.
Es ist einfach, das Input
struct Beispiel zu missbrauchen. Der Benutzer muss den Diskriminator korrekt verwenden, um auf das Element zuzugreifen, das die Daten enthält. Sie können vor Missbrauch schützen, indem Sie die unionprivate
speziellen Zugriffsfunktionen vornehmen und bereitstellen, wie im nächsten Beispiel gezeigt.
Uneingeschränkt union (C++11)
In C++03 und früheren Versionen kann eine union Nicht-Datenmember enthaltenstatic , die über einen class Typ verfügen, solange der Typ keine Constructors- oder De-Ors-Operatorenstructoder Zuordnungsoperatoren bereitgestellt hat. In C++11 wurden diese Einschränkungen entfernt. Wenn Sie ein solches Element in Ihr unioneinschließen, markiert der Compiler automatisch alle speziellen Memberfunktionen, die nicht vom Benutzer bereitgestellt werden.deleted
Wenn es union sich um eine anonyme union Person in einer class oder struct, dann werden alle speziellen Memberfunktionen der class bereitgestellten oder struct nicht angegebenen Benutzer als deleted
gekennzeichnet. Das folgende Beispiel zeigt, wie Sie diesen Fall behandeln. Eines der Mitglieder des union Mitglieds hat ein Mitglied, das diese besondere Behandlung erfordert:
// 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;
}
Ein union Verweis kann nicht gespeichert werden. A union unterstützt auch keine Vererbung. Dies bedeutet, dass Sie keine union Basis classverwenden können oder von einer anderen classerben oder virtuelle Funktionen haben können.
Initialisieren eines union
Sie können eine union in derselben Anweisung deklarieren und initialisieren, indem Sie einen Ausdruck in geschweifte Klammern zuweisen. Der Ausdruck wird ausgewertet und dem ersten Feld der .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
*/
Die NumericType
union Anordnung erfolgt im Arbeitsspeicher (konzeptionell) wie in der folgenden Abbildung dargestellt:
Das Diagramm zeigt 8 Byte Daten. Der doppelte Typ "dValue" belegt die gesamten 8 Byte. Der Typ long lValue belegt die ersten 4 Byte. Der kurze Typ "iValue" belegt das erste Byte.
Anonym union
Eine anonyme union Person wird ohne oder class-name
declarator-list
deklariert.
union {
member-list
}
Namen, die in einer anonymen union Person deklariert sind, werden direkt verwendet, z. B. Nichtmembervariablen. Es bedeutet, dass die in einer anonymen union Person deklarierten Namen im umgebenden Bereich eindeutig sein müssen.
Eine anonyme union Person unterliegt diesen Einschränkungen:
- Wenn sie im Datei- oder Namespacebereich deklariert ist, muss sie auch als
static
deklariert werden. - Er kann nur
public
Mitglieder haben, undprivate
protected
Mitglieder in einer anonymen union Datei generieren Fehler. - Sie kann keine Memberfunktionen haben.
Siehe auch
Feedback
https://aka.ms/ContentUserFeedback.
Bald verfügbar: Im Laufe des Jahres 2024 werden wir GitHub-Tickets als Feedbackmechanismus für Inhalte auslaufen lassen und es durch ein neues Feedbacksystem ersetzen. Weitere Informationen finden Sie unter:Feedback senden und anzeigen für