언제 노조를 사용하겠습니까? C- 전용 날의 잔재입니까? 나는 배웠지 만 실제로 노동 조합을

나는 배웠지 만 실제로 노동 조합을 얻지는 못한다. 내가 통과하는 모든 C 또는 C ++ 텍스트는 그것들을 소개하지만 (때로는 통과 할 때도 있지만) 왜 또는 어디에서 사용해야하는지에 대한 실제적인 예는 거의 없습니다. 현대 (또는 레거시) 사례에서 노동 조합은 언제 유용할까요? 내 두 가지 추측은 작업 할 공간이 매우 제한적이거나 API (또는 비슷한 것)를 개발할 때 최종 사용자가 여러 객체 / 유형의 인스턴스를 하나만 갖도록하려는 경우 마이크로 프로세서를 프로그래밍하는 것입니다. 한 번. 이 두 가지 추측이 오른쪽에 가깝습니까?



답변

노동 조합은 일반적으로 차별 자의 회사와 함께 사용됩니다. 노동 조합의 어느 분야가 유효한지를 나타내는 변수입니다. 예를 들어, 고유 한 변형 유형 을 생성한다고 가정 해 보겠습니다 .

struct my_variant_t {
    int type;
    union {
        char char_value;
        short short_value;
        int int_value;
        long long_value;
        float float_value;
        double double_value;
        void* ptr_value;
    };
};

그런 다음 다음과 같이 사용하십시오.

/* construct a new float variant instance */
void init_float(struct my_variant_t* v, float initial_value) {
    v->type = VAR_FLOAT;
    v->float_value = initial_value;
}

/* Increments the value of the variant by the given int */
void inc_variant_by_int(struct my_variant_t* v, int n) {
    switch (v->type) {
    case VAR_FLOAT:
        v->float_value += n;
        break;

    case VAR_INT:
        v->int_value += n;
        break;
    ...
    }
}

이것은 실제로 Visual Basic 내부에서 매우 일반적인 관용구입니다.

실제 예는 SDL의 SDL_Event union을 참조하십시오 . ( 실제 소스 코드는 여기 ). 거기에있다 type노조의 상단에있는 필드와 같은 필드는 모든 SDL_ * 이벤트 구조체에 반복됩니다. 그런 다음 올바른 이벤트를 처리하려면 type필드 값을 확인해야 합니다.

장점은 간단합니다. 불필요한 메모리를 사용하지 않고 모든 이벤트 유형을 처리 할 수있는 단일 데이터 유형이 있습니다.


답변

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에서는 공용체가 제한되지 않기 때문에 이것이 더 강력합니다. 자세한 내용 은 위의 문서 또는 이 기사연결된 문서 를 참조하십시오.


답변

하나의 예는 임베디드 영역에 있으며, 레지스터의 각 비트는 다른 의미를 가질 수 있습니다. 예를 들어, 8 비트 정수와 8 개의 개별 1 비트 비트 필드가있는 구조체를 결합하면 하나의 비트 또는 전체 바이트를 변경할 수 있습니다.


답변

Herb Sutter 는 약 6 년 전에 GOTW 에 다음과 같이 강조 했습니다.

“그러나 노조는 이전 시대에 비해 오래 지속 된 것이라고 생각하지 않습니다. 노조는 아마도 데이터가 겹치도록하여 공간을 절약하는 데 가장 유용 할 것입니다. 이것은 C ++ 과 오늘날의 현대 세계에서 여전히 바람직 합니다. 고급 C ++세계 표준 라이브러리 구현은 이제 “작은 문자열 최적화”를 구현하기 위해이 기술 만 사용합니다. “작은 문자열 최적화”는 문자열 객체 자체 내부의 저장소를 재사용하는 훌륭한 최적화 대안입니다. 큰 문자열의 경우 문자열 객체 내부의 공간은 동적으로의 일반적인 포인터를 저장합니다 할당 된 버퍼 및 버퍼의 크기와 같은 하우스 키핑 정보; 작은 문자열의 경우 문자열 공간을 직접 저장하고 동적 메모리 할당을 완전히 피하기 위해 동일한 공간이 대신 재사용됩니다. 작은 문자열 최적화 (및 다른 깊이의 다른 문자열 최적화 및 비관 화)에 대한 자세한 내용은 …을 참조하십시오. “

덜 유용한 예제를 보려면 길지만 결정적이지 않은 질문 gcc, 엄격 앨리어싱 및 노조를 통한 캐스팅을 참조하십시오 .


답변

글쎄, 내가 생각할 수있는 사용 사례의 예는 다음과 같습니다.

typedef union
{
    struct
    {
        uint8_t a;
        uint8_t b;
        uint8_t c;
        uint8_t d;
    };
    uint32_t x;
} some32bittype;

그런 다음 해당 32 비트 데이터 블록의 8 비트 별도 부분에 액세스 할 수 있습니다. 그러나 엔디안에 의해 물릴 가능성에 대비하십시오.

이것은 하나의 가상의 예일 뿐이지 만 필드의 데이터를 이와 같은 구성 요소 부분으로 분할 할 때마다 공용체를 사용할 수 있습니다.

즉, 엔디안 안전 방법이 있습니다.

uint32_t x;
uint8_t a = (x & 0xFF000000) >> 24;

예를 들어, 이진 연산은 컴파일러에 의해 올바른 엔디안으로 변환됩니다.


답변

노동 조합에 대한 일부 용도 :

  • 알 수없는 외부 호스트에 일반 엔디안 인터페이스를 제공하십시오.
  • 네트워크 링크에서 VAX G_FLOATS 를 수락 하고 처리 를 위해 IEEE 754 long reals 로 변환하는 것과 같은 외부 CPU 아키텍처 부동 소수점 데이터를 조작 합니다 .
  • 더 높은 수준의 유형에 대한 간단한 비트 트위들 링 액세스를 제공합니다.
union {
      unsigned char   byte_v[16];
      long double     ld_v;
 }

이 선언을 사용하면 a의 16 진수 바이트 값을 표시하거나 long double지수 기호를 변경하거나 비정규 값인지 확인하거나 지원하지 않는 CPU에 대해 긴 이중 산술을 구현하는 등의 작업이 간단합니다 .

  • 필드가 특정 값에 종속되는 경우 저장 공간 절약 :

    class person {
        string name;
    
        char gender;   // M = male, F = female, O = other  
        union {
            date  vasectomized;  // for males  
            int   pregnancies;   // for females  
        } gender_specific_data;
    }
  • 컴파일러에서 사용할 포함 파일을 정리하십시오. 수십에서 수백 가지 용도로 사용됩니다 union.

    [wally@zenetfedora ~]$ cd /usr/include
    [wally@zenetfedora include]$ grep -w union *
    a.out.h:  union
    argp.h:   parsing options, getopt is called with the union of all the argp
    bfd.h:  union
    bfd.h:  union
    bfd.h:union internal_auxent;
    bfd.h:  (bfd *, struct bfd_symbol *, int, union internal_auxent *);
    bfd.h:  union {
    bfd.h:  /* The value of the symbol.  This really should be a union of a
    bfd.h:  union
    bfd.h:  union
    bfdlink.h:  /* A union of information depending upon the type.  */
    bfdlink.h:  union
    bfdlink.h:       this field.  This field is present in all of the union element
    bfdlink.h:       the union; this structure is a major space user in the
    bfdlink.h:  union
    bfdlink.h:  union
    curses.h:    union
    db_cxx.h:// 4201: nameless struct/union
    elf.h:  union
    elf.h:  union
    elf.h:  union
    elf.h:  union
    elf.h:typedef union
    _G_config.h:typedef union
    gcrypt.h:  union
    gcrypt.h:    union
    gcrypt.h:    union
    gmp-i386.h:  union {
    ieee754.h:union ieee754_float
    ieee754.h:union ieee754_double
    ieee754.h:union ieee854_long_double
    ifaddrs.h:  union
    jpeglib.h:  union {
    ldap.h: union mod_vals_u {
    ncurses.h:    union
    newt.h:    union {
    obstack.h:  union
    pi-file.h:  union {
    resolv.h:   union {
    signal.h:extern int sigqueue (__pid_t __pid, int __sig, __const union sigval __val)
    stdlib.h:/* Lots of hair to allow traditional BSD use of `union wait'
    stdlib.h:  (__extension__ (((union { __typeof(status) __in; int __i; }) \
    stdlib.h:/* This is the type of the argument to `wait'.  The funky union
    stdlib.h:   causes redeclarations with either `int *' or `union wait *' to be
    stdlib.h:typedef union
    stdlib.h:    union wait *__uptr;
    stdlib.h:  } __WAIT_STATUS __attribute__ ((__transparent_union__));
    thread_db.h:  union
    thread_db.h:  union
    tiffio.h:   union {
    wchar.h:  union
    xf86drm.h:typedef union _drmVBlank {

답변

유니언은 바이트 수준 (낮은 수준) 데이터를 처리 할 때 유용합니다.

최근 사용 된 것 중 하나는 다음과 같은 IP 주소 모델링이었습니다.

// Composite structure for IP address storage
union
{
    // IPv4 @ 32-bit identifier
    // Padded 12-bytes for IPv6 compatibility
    union
    {
        struct
        {
            unsigned char _reserved[12];
            unsigned char _IpBytes[4];
        } _Raw;

        struct
        {
            unsigned char _reserved[12];
            unsigned char _o1;
            unsigned char _o2;
            unsigned char _o3;
            unsigned char _o4;
        } _Octet;
    } _IPv4;

    // IPv6 @ 128-bit identifier
    // Next generation internet addressing
    union
    {
        struct
        {
            unsigned char _IpBytes[16];
        } _Raw;

        struct
        {
            unsigned short _w1;
            unsigned short _w2;
            unsigned short _w3;
            unsigned short _w4;
            unsigned short _w5;
            unsigned short _w6;
            unsigned short _w7;
            unsigned short _w8;
        } _Word;
    } _IPv6;
} _IP;