C++ 联合体、结构体、成员类型

6
如果我有一个类:
class Odp
{
    int i;
    int b;
    union
    {
         long f;
         struct
         {
               WCHAR* pwszFoo;
               HRESULT hr;
         };
    };

}

联合(Union)表示,在所列出的所有值中,它一次只能采用其中一个值?在访问这些变量方面,它是如何工作的?我如何直接访问 hr?如果我设置了 hr,那么如果我尝试访问 f 会发生什么?

6个回答

9

这是C++标准中非常棘手的领域——基本上,根据标准,联合实例在任何时候只能被视为包含一个“活动”的成员——最后一个写入它的成员。因此:

union U {
   int a;
   char c;
};

那么:

U u;
u.a = 1;
int n = u.a;
u.c = 2;
char c = u.c;

是可以的,但是:

U u;
u.a = 1;
char c = u.c;

实际上,无论是使用nullptr还是NULL都没有问题。但是,已经有大量现有的代码表明两者都可以。在任何情况下,包括无效访问时,都不会抛出异常。C++语言异常处理非常少用。

基本上,如果你在C++代码中使用union来处理除了C库之外的任何东西,那么就有问题了。


+1 -- 但即使对于C库,大多数情况下也可以使用reinterpret_cast而不是联合。 - Billy ONeal

4
每次你设置(写入)联合体的成员时,你实际上使其“活动”。你只能读取当前活动的联合体成员。这意味着你需要记住每个时刻哪个成员是活动的。
尝试访问非活动成员会导致未定义的行为。
还要注意,你的代码不符合C++标准。C++中没有“匿名结构体”的概念。你的结构体成员必须有一个名称。如果你的编译器接受它,那么它只是你特定编译器支持的非标准扩展。

嗯...我知道结果会返回未定义的数据,但据我所知,访问其他成员不应该像访问空指针的目标那样导致未定义行为。 - Billy ONeal
3
@Billy ONeal:是的,出于显而易见的原因,应该这样做。由于这些类型通常没有关系,因此其他(非活动)成员很容易出现陷阱表示,这就是行为未定义的主要和明显的原因。 - AnT stands with Russia
2
“trap representation” <-- 以前从未听说过这个。+1 - Billy ONeal

2

使用 union 时,同一块内存空间将用于表示任何给定时间的单个成员。因此,如果您拥有联合体的实例并设置了 hr 的值,那么如果您尝试读取 f 的值,则会得到垃圾值。

尝试使用以下方法访问 hr

union a;
a.hr = NULL;

那么这是程序员的责任来防范它,还是会抛出异常? - Nick Heiner
@Rosarch:这是C语言的一个特性;C语言没有异常处理机制。使用联合体需要自行承担风险 :) - Billy ONeal
不会抛出异常;程序员需要确保它们正在访问 union 的正确成员。 - Justin Ethier

1

在这种情况下,结构体没有名称。我该如何访问它? - Nick Heiner
1
@Rosarch:在 C 语言中,从技术上讲这是非法的。但在 C++ 中是合法的,此时名称会作为 class Odp 的成员暴露出来。 - Billy ONeal
1
@Billy ONeal:不正确。在C++中,“匿名结构体”是非法的。C++支持匿名联合体,但不支持匿名结构体。OP的声明是不合法的。 - AnT stands with Russia
@John Weldon:你回答中的链接没有提供任何信息,直到你看到“jsmith”的帖子,他正确地指出了代码是非法的。 - AnT stands with Russia
1
@John Weldon:在您的答案中,Odp o1(); 声明了一个函数,而不是一个对象。我认为这不是您的意图。 - AnT stands with Russia
显示剩余2条评论

0
联合体将为联合体中最大的类型(thing)分配足够的内存。因此,您可能有许多具有大型内存占用的对象类型,并且您只会一次将其中一个传递到代码中的其他位置。联合体使您能够这样做。它比传递void指针高一步。在任何情况下,您都需要想出一种方法来知道存储在联合体实例中的内容。下面的代码是通过将联合体包装在结构中来实现这一点的简单方法。该结构定义了一个枚举以标识联合体使用的项目,并提供了一个存储枚举类型的位置。
    union UnionItem {
      int a;
      float b;
      double c;
   };

   struct UnionObj {
      enum Type{
         I, F, D
      };
      Type t;
      UnionItem item;
   };

   UnionObj o;
   o.item.b = 2.3f;
   o.t = UnionObj::F;

   // Usually the UnionObj would be passed to other functions or methods.

   switch (o.t) {
   case UnionObj::I:
      cout << o.item.a;
      break;
   case UnionObj::F:
      cout << o.item.b;
      break;
   case UnionObj::D:
      cout << o.item.c;
      break;
   default:
      cout << "Something wrong!";
   }

0

尝试访问“f”将会给你一些结果。它很可能是联合中其他成员作为“f”的数据类型的表示形式,即在这种情况下,你可能会读取“pwszFoo”表示为“long”数据类型的部分或全部内容。通用概念很简单-联合成员在内存中共享相同的位置。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接