union

Hinweis

In C++17 und höher ist dies std::variantclass 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

uniontagopt{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 Inputstruct 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 Inputstruct 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 deletedgekennzeichnet. 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 NumericTypeunion Anordnung erfolgt im Arbeitsspeicher (konzeptionell) wie in der folgenden Abbildung dargestellt:

Diagram that shows the overlapping storage of data in the NumericType union.

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-namedeclarator-listdeklariert.

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 staticdeklariert werden.
  • Er kann nur public Mitglieder haben, und privateprotected Mitglieder in einer anonymen union Datei generieren Fehler.
  • Sie kann keine Memberfunktionen haben.

Siehe auch

Klassen und Strukturen
Schlüsselwörter
class
struct