C ++ 공용체가 꽤 멋지다는 것을 알았습니다. 사람들은 일반적으로 "제자리에서"통합 인스턴스의 값을 변경하려는 유스 케이스 만 생각하는 것 같습니다 (메모리를 절약하거나 의심스러운 변환을 수행하는 데만 도움이 됨).
실제로 Union 인스턴스의 가치를 절대로 변경하지 않더라도 Union 은 소프트웨어 엔지니어링 도구로서 큰 힘을 발휘할 수 있습니다 .
사용 사례 1 : 카멜레온
공용체를 사용하면 하나의 명칭으로 여러 개의 임의 클래스를 다시 그룹화 할 수 있습니다. 기본 클래스 및 파생 클래스의 경우와 유사하지 않습니다. 그러나 변경 사항은 주어진 통합 인스턴스로 할 수 있고 할 수없는 것입니다.
struct Batman;
struct BaseballBat;
union Bat
{
Batman brucewayne;
BaseballBat club;
};
ReturnType1 f(void)
{
BaseballBat bb = {/* */};
Bat b;
b.club = bb;
// do something with b.club
}
ReturnType2 g(Bat& b)
{
// do something with b, but how do we know what's inside?
}
Bat returnsBat(void);
ReturnType3 h(void)
{
Bat b = returnsBat();
// do something with b, but how do we know what's inside?
}
프로그래머는 주어진 유니온 인스턴스의 컨텐츠 유형을 사용하고자 할 때 특정 유형이어야합니다. f
위의 기능에 해당 합니다. 그러나 함수가 g
위 의 경우와 같이 전달 된 인수로 통합 인스턴스를 수신하면 함수와 함께 무엇을 해야할지 알 수 없습니다. 공용체 인스턴스를 반환하는 함수에도 동일하게 적용됩니다.h
. 호출자는 내부 내용을 어떻게 알 수 있습니까?
통합 인스턴스가 인수 또는 반환 값으로 전달되지 않으면 프로그래머가 내용을 변경하기로 선택했을 때 매우 급격한 삶을 살 수밖에 없습니다.
Batman bm = {/* */};
Baseball bb = {/* */};
Bat b;
b.brucewayne = bm;
// stuff
b.club = bb;
그리고 이것이 가장 인기있는 노동 조합의 사용 사례입니다. 또 다른 유스 케이스는 통합 인스턴스가 유형을 알려주는 무언가와 함께 제공되는 경우입니다.
사용 사례 2 : "니스 난, 당신을 만나서 object
에서,Class
"
프로그래머가 유니언 인스턴스를 항상 타입 디스크립터와 짝짓기로 선택했다고 가정 해 보자 (그러한 객체에 대한 구현을 상상하기 위해 독자의 재량에 맡기겠다). 이것은 프로그래머가 원하는 것이 메모리를 절약하고 타입 디스크립터의 크기가 유니온의 크기와 관련하여 무시할 수없는 경우 유니온 자체의 목적을 무효화합니다. 그러나 통합 인스턴스가 내부에 무엇이 있는지 모르는 수신자 또는 호출자에게 인수 또는 반환 값으로 전달 될 수 있어야한다고 가정 해 봅시다.
그런 다음 프로그래머는 switch
Bruce Wayne에게 나무 막대기 또는 이와 동등한 것을 구별하기 위해 제어 흐름 설명을 작성해야합니다. 노조에 두 가지 유형의 내용 만있을 때 그리 나쁘지는 않지만 분명히 노조는 더 이상 확장되지 않습니다.
사용 사례 3 :
ISO C ++ 표준 에 대한 권장 사항 작성자가 2008 년에 다시 제안한 것처럼,
많은 중요한 문제 도메인에는 많은 수의 객체 또는 제한된 메모리 리소스가 필요합니다. 이러한 상황에서 공간 보존은 매우 중요하며, 노조가 종종이를 수행하는 완벽한 방법입니다. 실제로 일반적인 유스 케이스는 노조가 수명 동안 활성 멤버를 변경하지 않는 상황입니다. 마치 하나의 멤버 만 포함 된 구조체 인 것처럼 구성, 복사 및 제거 할 수 있습니다. 이것의 전형적인 적용은 동적으로 할당되지 않는 이종 유형의 관련되지 않은 유형의 콜렉션을 작성하는 것입니다 (아마도 이들은 맵 또는 배열의 멤버로 제자리에 구성되어 있음).
이제 UML 클래스 다이어그램을 사용한 예제가 있습니다.
일반 영어로 된 상황 : 클래스 A의 객체는 B1, ..., Bn 사이의 모든 클래스의 객체를 가질 수 있으며 각 유형 중 최대 하나는 n 이 10보다 큰 숫자입니다.
다음과 같이 필드 (데이터 멤버)를 A에 추가하고 싶지 않습니다.
private:
B1 b1;
.
.
.
Bn bn;
n 이 다를 수 있기 때문에 (믹스에 Bx 클래스를 추가하고 싶을 수도 있음) 생성자와 혼동을 일으키고 A 객체가 많은 공간을 차지하기 때문입니다.
우리는 캐스트 void*
가있는 Bx
객체 에 대한 포인터 의 이상한 컨테이너를 사용 하여 객체를 검색 할 수는 있지만 모호하고 C 스타일입니다 ... 그러나 더 중요한 것은 동적으로 할당 된 많은 객체의 수명을 관리 할 수있게 해줍니다.
대신 수행 할 수있는 작업은 다음과 같습니다.
union Bee
{
B1 b1;
.
.
.
Bn bn;
};
enum BeesTypes { TYPE_B1, ..., TYPE_BN };
class A
{
private:
std::unordered_map<int, Bee> data; // C++11, otherwise use std::map
public:
Bee get(int); // the implementation is obvious: get from the unordered map
};
그런 다음에서 노조 인스턴스의 컨텐츠를 얻기 위해 data
당신이 사용 a.get(TYPE_B2).b2
하고 좋아하는, a
클래스입니다 A
인스턴스를.
C ++ 11에서는 공용체가 제한되지 않기 때문에 이것이 더 강력합니다. 자세한 내용 은 위의 문서 또는 이 기사 에 연결된 문서 를 참조하십시오.