Share via


union

Catatan

Di C++17 dan yang lebih baru, std::variantclass adalah alternatif jenis aman untuk union.

adalah union jenis yang ditentukan pengguna di mana semua anggota berbagi lokasi memori yang sama. Definisi ini berarti bahwa pada waktu tertentu, dapat union berisi tidak lebih dari satu objek dari daftar anggotanya. Ini juga berarti bahwa tidak peduli berapa banyak anggota yang union dimiliki, itu selalu menggunakan hanya cukup memori untuk menyimpan anggota terbesar.

Dapat union berguna untuk menghemat memori ketika Anda memiliki banyak objek dan memori terbatas. Namun, union perlu perawatan ekstra untuk digunakan dengan benar. Anda bertanggung jawab untuk memastikan bahwa Anda selalu mengakses anggota yang sama dengan yang Anda tetapkan. Jika ada jenis anggota yang memiliki konsekuensistructnontrivial atau, maka Anda harus menulis kode untuk secara eksplisit construct dan menghancurkan anggota tersebut. Sebelum Anda menggunakan union, pertimbangkan apakah masalah yang coba Anda selesaikan dapat diekspresikan dengan lebih baik dengan menggunakan jenis dasar class dan turunan class .

Sintaks

uniontagopt{member-list};

Parameter

tag
Nama jenis yang diberikan kepada union.

member-list
Anggota yang dapat berisi union .

Mendeklarasikan union

Mulai deklarasi dengan union menggunakan union kata kunci, dan sertakan daftar anggota dalam kurung kurawal:

// 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
}

Menggunakan union

Dalam contoh sebelumnya, kode apa pun yang mengakses union kebutuhan untuk mengetahui anggota mana yang menyimpan data. Solusi paling umum untuk masalah ini disebut diskriminasi union. Ini mencakup union dalam struct, dan menyertakan enum anggota yang menunjukkan jenis anggota yang saat ini disimpan di union. Contoh berikut menunjukkan pola dasar:

#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;
}

Dalam contoh sebelumnya, union dalamstructInputtidak memiliki nama, sehingga disebut anonimunion. Anggotanya dapat diakses langsung seolah-olah mereka adalah anggota struct. Untuk informasi selengkapnya tentang cara menggunakan anonim union, lihat bagian Anonim union .

Contoh sebelumnya menunjukkan masalah yang juga dapat Anda selesaikan dengan menggunakan class jenis yang berasal dari basis classumum . Anda dapat mencabangkan kode Anda berdasarkan jenis runtime setiap objek dalam kontainer. Kode Anda mungkin lebih mudah dipertahankan dan dipahami, tetapi mungkin juga lebih lambat daripada menggunakan union. Selain itu union, dengan , Anda dapat menyimpan jenis yang tidak terkait. A union memungkinkan Anda mengubah jenis nilai yang disimpan secara dinamis tanpa mengubah jenis variabel itu union sendiri. Misalnya, Anda dapat membuat array MyUnionTypeheterogen , yang elemennya menyimpan nilai yang berbeda dari berbagai jenis.

Mudah untuk menyalahgunakan Inputstruct dalam contoh. Terserah pengguna untuk menggunakan diskriminator dengan benar untuk mengakses anggota yang menyimpan data. Anda dapat melindungi dari penyalahgunaan unionprivate dengan membuat dan menyediakan fungsi akses khusus, seperti yang ditunjukkan pada contoh berikutnya.

Tidak dibatasi union (C++11)

Di C++03 dan yang lebih lama, union dapat berisi anggota nonstatic data yang memiliki class jenis, selama jenis tidak memiliki pengguna yang menyediakan constructataus, destructors, atau operator penugasan. Di C++11, pembatasan ini dihapus. Jika Anda menyertakan anggota seperti itu dalam , unionpengkompilasi secara otomatis menandai fungsi anggota khusus yang tidak disediakan pengguna sebagai deleted. union Jika adalah anonim union di dalam class atau struct, maka fungsi anggota khusus dari class atau struct yang tidak disediakan pengguna ditandai sebagai deleted. Contoh berikut menunjukkan cara menangani kasus ini. Salah satu anggota union memiliki anggota yang memerlukan perlakuan khusus ini:

// 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;
}

union Tidak dapat menyimpan referensi. Juga union tidak mendukung pewarisan. Itu berarti Anda tidak dapat menggunakan union sebagai basis class, atau mewarisi dari yang lain class, atau memiliki fungsi virtual.

Menginisialisasi union

Anda dapat mendeklarasikan dan menginisialisasi union dalam pernyataan yang sama dengan menetapkan ekspresi yang diapit kurung kurawal. Ekspresi dievaluasi dan ditetapkan ke bidang pertama dari 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
*/

NumericTypeunion diatur dalam memori (secara konseptual) seperti yang ditunjukkan pada gambar berikut:

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

Diagram menunjukkan 8 byte data. Jenis ganda dValue menempati seluruh 8 byte. Jenis panjang lValue menempati 4 byte pertama. Jenis pendek iValue menempati byte pertama.

Anonim union

Anonim union adalah satu dinyatakan tanpa class-name atau declarator-list.

union { member-list }

Nama yang dideklarasikan dalam anonim union digunakan secara langsung, seperti variabel nonmember. Ini menyiratkan bahwa nama-nama yang dideklarasikan dalam anonim union harus unik dalam cakupan di sekitarnya.

Anonim union tunduk pada pembatasan ini:

  • Jika dideklarasikan dalam cakupan file atau namespace layanan, itu juga harus dinyatakan sebagai static.
  • Ini hanya public dapat memiliki anggota; memiliki private anggota dan protected dalam anonim union menghasilkan kesalahan.
  • Ini tidak dapat memiliki fungsi anggota.

Baca juga

Kelas dan Struktur
Kata kunci
class
struct