union

참고 항목

C++17 이상 std::variantclass 에서는 형식이 안전한 대안입니다 union.

A union 는 모든 멤버가 동일한 메모리 위치를 공유하는 사용자 정의 형식입니다. 이 정의는 지정된 시간에 union 멤버 목록에서 하나 이상의 개체를 포함할 수 있음을 의미합니다. 또한 멤버 수가 아무리 많더라도 항상 최대 멤버를 union 저장하기에 충분한 메모리만 사용한다는 의미입니다.

A는 union 개체가 많고 메모리가 제한된 경우 메모리를 절약하는 데 유용할 수 있습니다. 그러나 union 올바르게 사용하려면 주의해야 합니다. 항상 할당한 동일한 멤버에 액세스할 수 있도록 해야 합니다. 멤버 형식에 사소한 단점struct이 있거나 해당 멤버를 명시적으로 구성struct 하고 삭제하는 코드를 작성해야 합니다. 사용하기 union전에 기본 class 및 파생 class 형식을 사용하여 해결하려는 문제를 더 잘 표현할 수 있는지 여부를 고려합니다.

구문

uniontagopt{member-list};

매개 변수

tag
에 지정된 형식 이름입니다 union.

member-list
포함할 union 수 있는 멤버입니다.

A 선언 union

키워드(keyword) 사용하여 선언을 unionunion 시작하고 멤버 목록을 중괄호로 묶습니다.

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

다음을 사용합니다. union

이전 예제에서 액세스하는 모든 코드는 union 데이터를 보유하는 멤버를 알아야 합니다. 이 문제에 대한 가장 일반적인 해결 방법을 차별이라고 union합니다. 인을 묶 unionstruct고 현재 에 저장된 멤버 형식을 나타내는 멤버를 union포함합니다enum. 다음 예제에서는 기본 패턴을 보여 줍니다.

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

이전 예제에서 in은 unionInputstruct 이름이 없으므로 익명union이라고 합니다. 해당 멤버는 마치 해당 멤버인 struct것처럼 직접 액세스할 수 있습니다. 익명을 사용하는 방법에 대한 자세한 내용은 익명 unionunion 섹션을 참조하세요.

이전 예제에서는 공통 기반class에서 파생되는 형식을 사용하여 class 해결할 수도 있는 문제를 보여 줍니다. 컨테이너에 있는 각 개체의 런타임 형식에 따라 코드를 분기할 수 있습니다. 코드를 기본 파악하고 이해하는 것이 더 쉬울 수 있지만, 코드를 사용하는 union것보다 느려질 수도 있습니다. 또한 관련 없는 형식을 union저장할 수 있습니다. A union 를 사용하면 변수의 형식 자체를 변경하지 않고 저장된 값의 형식을 union 동적으로 변경할 수 있습니다. 예를 들어 요소가 다른 형식의 MyUnionType다른 값을 저장하는 이질적인 배열을 만들 수 있습니다.

이 예제에서는 쉽게 오용할 수 Inputstruct 있습니다. 판별자를 올바르게 사용하여 데이터를 보유하는 멤버에 액세스하는 것은 사용자의 달려 있습니다. 다음 예제와 같이 특수 액세스 함수를 만들고 unionprivate 제공하여 오용으로부터 보호할 수 있습니다.

무제한 union (C++11)

C++03 및 이전 union 버전에서는 형식에 사용자가 제공한 constructors, destructors 또는 대입 연산자가 없는 한 형식이 있는 비static 데이터 멤버 class 를 포함할 수 있습니다. C++ 11에서는 이러한 제한이 제거됩니다. 이러한 멤버를 포함하는 union경우 컴파일러는 사용자가 제공하지 deleted않은 특수 멤버 함수를 자동으로 표시합니다. 익명 unionunionclass 인 경우 또는 struct사용자가 제공하지 않은 특수 멤버 함수 classstruct 는 다음과 같이 deleted표시됩니다. 다음 예제에서는 이 사례를 처리하는 방법을 보여줍니다. 멤버 union 중 하나에는 이 특별한 처리가 필요한 멤버가 있습니다.

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

A는 union 참조를 저장할 수 없습니다. 또한 A union 는 상속을 지원하지 않습니다. 즉, 기본class으로 사용 union 하거나 다른 class함수에서 상속하거나 가상 함수를 가질 수 없습니다.

를 초기화합니다 union

중괄호로 묶인 union 식을 할당하여 동일한 문에서 선언하고 초기화할 수 있습니다. 식이 계산되고 첫 번째 필드에 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 그림과 같이 메모리에 정렬됩니다(개념적으로).

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

다이어그램은 8바이트의 데이터를 보여줍니다. 이중 형식 dValue는 전체 8바이트를 차지합니다. long lValue 형식은 처음 4바이트를 차지합니다. iValue가 첫 번째 바이트를 차지하는 짧은 형식입니다.

익명 union

익명 union 은 1 또는 declarator-list0 없이 선언된 익명입니다class-name.

union { member-list }

익명 union 으로 선언된 이름은 비회원 변수와 같이 직접 사용됩니다. 이는 익명 union 으로 선언된 이름이 주변 범위에서 고유해야 임을 의미합니다.

익명 union 에는 다음 제한 사항이 적용됩니다.

  • 파일 또는 네임스페이스 범위 static에서 선언된 경우 .
  • 멤버만 public 가질 수 있습니다. 익명 union 의 멤버가 protected 있으면 private 오류가 발생합니다.
  • 멤버 함수를 가질 수 없습니다.

참고 항목

클래스 및 구조체
키워드
class
struct