用法
声明联合体类型
联合体是一个特殊的类,他在同一时刻只能保持有一个非静态数据成员
语法
union attr class-head-name { member-specification }
- class-head-name:所定义的联合体的名字。可以前附嵌套名说明符(名字与作用域解析运算符的序列,以作用域解析运算符::结尾)。可以忽略名字,此时联合体是无名 的
- member-specification:访问说明符(access specifiers)、成员对象和成员函数的声明与定义的列表。
- 联合体可以拥有成员函数(包含构造函数和析构函数),但不能有虚函数。(必须是self closure)
- 联合体不能有基类且不能用作基类。
- 联合体不能拥有引用类型的非静态数据成员。(不能有别名,exclusive)
- 正如结构体的声明中一般,联合体的默认成员访问是 public。
解释
- 联合体的大小至少足以保有其最大的数据成员,但通常不会更大。
- 其他数据成员intend to分配于the same bytes as part of that largest member。
- 分配的细节由我们implementation-defined,但所有非静态数据成员均拥有相同的地址。
- 读取并非最近written的联合体成员是undefined行为。
- 许多编译器以非标准语言扩展实现读取联合体的不活跃成员的能力。
union S
{
std::int32_t n; // 4 bytes
std::uint16_t s[2]; // 2*2=4 bytes
std::uint8_t c; // 1 bytes
};
int main()
{
S n={0x12345678}; //S.n is active member
// 该时刻不能读S.s和S.c,因为这两个不是active member
std::cout<<std::hex<<"s.n="<<s.n<<'\n';
S.s[0]=0x0011; //S.s is active member
// 该时刻不能读S.n和S.c了
std::cout << "s.c is now " << +s.c << '\n' // 11 or 00, depending on platform
<< "s.n is now " << s.n << '\n'; // 12340011 or 00115678
}
每个成员在内存中的分配就好像他是class中的唯一成员一样
成员生存期
联合体成员的生存期从该成员被设为活跃时开始。如果之前已经有另一成员活跃,那么此前已有的这一成员生存期终止。
union A {int x; int y[4];};
struct B {A a;};
union C { B b; int k;};
int f()
{
C c; // 不开始任何联合体成员的生存期
c.b.a.y[3]=5; // 开始c.b和c.b.a.y成员生存期
return c.b.a.y[3];
}
struct X { const int a; int b; };
union Y { X x; int k; };
void g()
{
Y y={{1,2}}; // y.x is active union member
int n=y.x.a;
y.k=4; // ends lifetime of y.x
y.x.b=n; // undefined behavior,因为y.x已经不存在了!
}
匿名联合体
匿名联合体有更多限制:
- 不能有成员函数
- 不能有静态数据成员
- 所有数据成员必须公开。
int main()
{
union
{
int a;
const char* p;
}
a=1;
p="Jennifer"
}
union-like classes
联合体式的类 是联合体,或是至少拥有一个匿名联合体成员的类。联合体式的类拥有一组变体成员(variant members):
- 其成员匿名联合体中的非静态数据成员;
- 如果联合体式的类是联合体,那么variant members是其并非匿名联合体的非静态数据成员。
struct S
{
enum {CHAR, INT, DOUBLE} tag;
union
{
char c;
int i;
double d;
};
};
void print_s(const S& s)
{
switch(s.tag)
{
case S::CHAR: std::cout<<s.c<<std::endl; break;
case S::INT: std::cout<<s.i<<std::endl; break;
case S::DOUBLE: std::cout<<s.d<<std::endl; break;
}
}
elaborated type specifier
若存在于作用域中的某个函数或变量所拥有的名字与某个联合体类型的名字相同,则可在该名字之前加上 union 以消歧义