【问题标题】:Unions that contain a"type" member包含“类型”成员的联合
【发布时间】:2011-02-09 00:14:05
【问题描述】:

我有一个关于工会的问题,我仍然不了解。我已经阅读了很多关于它们的用途,并且在大多数情况下可以看到它们如何有用并理解它们。我已经看到它们可以提供原始的“C 风格”多态性。我在几个网站上看到的这个例子是 SDL 的事件联合:

typedef union {
 Uint8 type;
 SDL_ActiveEvent active;
 SDL_KeyboardEvent key;
 SDL_MouseMotionEvent motion;
 SDL_MouseButtonEvent button;
 SDL_JoyAxisEvent jaxis;
 SDL_JoyBallEvent jball;
 SDL_JoyHatEvent jhat;
 SDL_JoyButtonEvent jbutton;
     SDL_ResizeEvent resize;
 SDL_ExposeEvent expose;
 SDL_QuitEvent quit;
 SDL_UserEvent user;
     SDL_SysWMEvent syswm;
} SDL_Event;

我无法理解的是,那里怎么会有一个“类型”成员与事件类型共存?由于它们占用相同的内存区域,因此它们不是一次只允许存在一个吗?联合不会在任何时候作为一种类型或事件之一存在吗?

我了解每个事件实际上都是一个带有类型成员的结构,例如:

// SDL_MouseButtonEvent

typedef struct{
     Uint8 type;
     Uint8 button;
     Uint8 state;
     Uint16 x, y;
} SDL_MouseButtonEvent;

这有什么意义?这是否允许联合的类型成员代表联合当前的任何结构的类型?当联合的每个成员(除了一个之外)都是结构并且每个结构都包含该成员时,这是否会发生某种奇怪的效果?

你可以在不知道对象是哪个结构的情况下访问结构成员吗?

谢谢!

【问题讨论】:

  • 请确认Uint8 type 字段与其他字段相关。我认为type 字段与其他字段无关。这只是另一个领域。

标签: c++ c unions


【解决方案1】:

如果每个事件类型都有一个Uint8 作为其第一个数据成员,那么uniontype 成员只是一种方便。

联合的一般规则是,您只能使用您写入的最后一个数据成员访问联合中的数据。所以,如果你上次写信给active,你下次就不能再读key了。

此规则的一个例外是,如果联合的多个成员共享相同的前缀(如果它们的第一个数据成员相同),则您可以通过共享的联合的任何数据成员访问该前缀前缀。因此,在这里,您可以参考active.typekey.type,无论联合的哪个数据成员处于活动状态,它都会起作用。

SDL_Event 的成员type 只是一个方便的快捷方式,它允许您访问该type 字段,而无需将其限定为event_object.active.typeevent_object.key.type。你可以使用event_object.type

【讨论】:

  • +1,简洁,我今天学到的第二个有用的东西......我想当你想到它时很明显,但有时它是我想念的明显的东西......:)跨度>
  • 我仍然觉得这个结构不寻常,我以前见过这样的东西:struct SDLEvent { Uint8 type; union { type1 t1; type2 t2... } };
  • @David:我也觉得这有点不寻常;我本来打算建议这种方法,但后来想知道 SDL 采用的方法是否有一些好处。我想不出有什么好处,但我真的不是工会方面的专家;我从来没有真正使用过它们。也许这只是一个古怪的设计选择......
  • 一方面,SDL 的做法允许在 SDLEvent*SDLActiveEvent* 之间进行直接的 C 风格转换,等等。(另一种方式也可以,但需要更多的输入。)
  • @James:有趣!但是有多少成员有资格成为“几个”(相对于“一个工会的几个成员共享相同的前缀”)。
【解决方案2】:

让我们看看 union 在内存中是如何布局的:

Address:    Uint8    MouseButtonEvent   KeyboardEvent
x+0x0       type     type               type
x+0x1       -        button             ?
x+0x2       -        state              ?
...

碰巧type 成员都排成一列,所以不管它是什么类型,作为Uint8 访问联合将产生事件的实际类型。

【讨论】:

    【解决方案3】:

    如果每个 SDL_xyz 结构的前几个字节都是它们自己的类型字段,这意味着当联合包含其中一个对象时,联合的前几个字节与 SDL 结构的前几个字节相同 - 即类型字段。

    联合不包含两者,它只包含第一个成员在类型、大小和位置上恰好与“类型”字段重合的 SDL 对象。

    【讨论】:

      【解决方案4】:

      标准要求 C/C++ 中的联合必须与它们包含的最严格的类型对齐。此外,由于结构的成员不能重新排序,并且由于当前标准的要求(在 C++0x 中更改)联合仅包含 POD 类型,因此想法是联合的类型成员映射到它包含的结构甲板。

      【讨论】:

        【解决方案5】:

        就联合而言,结构和原始类型之间没有真正的区别。 SDL_MouseButtonEven 结构只是一堆一个接一个的类型。
        联合的type 成员代替了事件结构的type 成员。用图形表示,它看起来像这样:

        SDL_Event union:
          type:    |--------|
          motion:  |--type--|-button-|--state-|-------x--------|-------y--------|  
          active:  |--type--|----------something-else-of-another-event--|          
          key:     |--type--|--maybe-a-smaller-event-|                             
          ... etc'
        

        【讨论】:

          【解决方案6】:

          [已编辑,感谢 James 的评论。]

          标准对联合做了一些特殊的保证,所以这种事情是安全的。

          重要的规则是,如果联合中的两个 POD 结构以相同的成员类型开始,则允许使用共享成员的“公共初始序列”。因此,如果设置了key.type,则读取button.type 是合法的,并且它将具有相同的值。

          此外,直接在联合中的普通数据成员(基本类型)type 必须在内存中布局,就好像它在仅包含该成员的 struct 中一样。也就是说type也等价于key.typebutton.type等。

          【讨论】:

          • 据我了解,对于 POD 联合,它是完全有保证的,因为“每个数据成员的分配就像它是结构的唯一成员一样”(C++03 9.5/1)。所有数据成员必须具有相同的地址,因为在结构的第一个成员之前不能有未命名的填充。
          • @James Aha,谢谢!我不知道我从哪里得出这个结论,除非阅读 9.2 并在 9.5 中错过了那句话。
          猜你喜欢
          • 1970-01-01
          • 2018-10-01
          • 1970-01-01
          • 2019-01-01
          • 2023-03-03
          • 2014-11-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多