union
참고 항목
C++17 이상 std::variant
class 에서는 형식이 안전한 대안입니다 union.
A union
는 모든 멤버가 동일한 메모리 위치를 공유하는 사용자 정의 형식입니다. 이 정의는 지정된 시간에 union 멤버 목록에서 하나 이상의 개체를 포함할 수 있음을 의미합니다. 또한 멤버 수가 아무리 많더라도 항상 최대 멤버를 union 저장하기에 충분한 메모리만 사용한다는 의미입니다.
A는 union 개체가 많고 메모리가 제한된 경우 메모리를 절약하는 데 유용할 수 있습니다. 그러나 union 올바르게 사용하려면 주의해야 합니다. 항상 할당한 동일한 멤버에 액세스할 수 있도록 해야 합니다. 멤버 형식에 사소한 단점struct이 있거나 해당 멤버를 명시적으로 구성struct 하고 삭제하는 코드를 작성해야 합니다. 사용하기 union전에 기본 class 및 파생 class 형식을 사용하여 해결하려는 문제를 더 잘 표현할 수 있는지 여부를 고려합니다.
구문
union
tag
opt{
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은 unionInput
struct 이름이 없으므로 익명union이라고 합니다. 해당 멤버는 마치 해당 멤버인 struct것처럼 직접 액세스할 수 있습니다. 익명을 사용하는 방법에 대한 자세한 내용은 익명 unionunion 섹션을 참조하세요.
이전 예제에서는 공통 기반class에서 파생되는 형식을 사용하여 class 해결할 수도 있는 문제를 보여 줍니다. 컨테이너에 있는 각 개체의 런타임 형식에 따라 코드를 분기할 수 있습니다. 코드를 기본 파악하고 이해하는 것이 더 쉬울 수 있지만, 코드를 사용하는 union것보다 느려질 수도 있습니다. 또한 관련 없는 형식을 union저장할 수 있습니다. A union 를 사용하면 변수의 형식 자체를 변경하지 않고 저장된 값의 형식을 union 동적으로 변경할 수 있습니다. 예를 들어 요소가 다른 형식의 MyUnionType
다른 값을 저장하는 이질적인 배열을 만들 수 있습니다.
이 예제에서는 쉽게 오용할 수 Input
struct 있습니다. 판별자를 올바르게 사용하여 데이터를 보유하는 멤버에 액세스하는 것은 사용자의 달려 있습니다. 다음 예제와 같이 특수 액세스 함수를 만들고 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
*/
다음 NumericType
union 그림과 같이 메모리에 정렬됩니다(개념적으로).
다이어그램은 8바이트의 데이터를 보여줍니다. 이중 형식 dValue는 전체 8바이트를 차지합니다. long lValue 형식은 처음 4바이트를 차지합니다. iValue가 첫 번째 바이트를 차지하는 짧은 형식입니다.
익명 union
익명 union 은 1 또는 declarator-list
0 없이 선언된 익명입니다class-name
.
union {
member-list
}
익명 union 으로 선언된 이름은 비회원 변수와 같이 직접 사용됩니다. 이는 익명 union 으로 선언된 이름이 주변 범위에서 고유해야 임을 의미합니다.
익명 union 에는 다음 제한 사항이 적용됩니다.
- 파일 또는 네임스페이스 범위
static
에서 선언된 경우 . - 멤버만
public
가질 수 있습니다. 익명 union 의 멤버가protected
있으면private
오류가 발생합니다. - 멤버 함수를 가질 수 없습니다.
참고 항목
피드백
https://aka.ms/ContentUserFeedback
출시 예정: 2024년 내내 콘텐츠에 대한 피드백 메커니즘으로 GitHub 문제를 단계적으로 폐지하고 이를 새로운 피드백 시스템으로 바꿀 예정입니다. 자세한 내용은 다음을 참조하세요.다음에 대한 사용자 의견 제출 및 보기