C++联合体union⽤法实例详解
本⽂实例讲述了C++联合体union⽤法。分享给⼤家供⼤家参考。具体如下:
我们应该按照C中的convention去使⽤union,这是我这篇⽂章要给出的观点。虽然C++使得我们可以扩展⼀些新的东西进去,但是,我建议你不要那样去做,看完这篇⽂章之后,我想你
⼤概也是这么想的。
  C由于没有类的概念,所有类型其实都可以看作是基本类型的组合,因此在union中包含struct也就是⼀件很⾃然的事情了,到了C++之后,既然普遍认为C++中的struct与class基本等价,那么union中是否可以有类成员呢?先来看看如下的代码:
struct TestUnion
{
    TestUnion() {}
};
typedef union
{
    TestUnion obj;
} UT;
int main (void)
{
    return 0;
}
  编译该程序,我们将被告知:
  error C2620: union '__unnamed' : member 'obj' has user-defined constructor or non-trivial default constructor
  ⽽如果去掉那个什么也没⼲的构造函数,则⼀切OK。
  为什么编译器不允许我们的union成员有构造函数呢?我⽆法到关于这个问题的⽐较权威的解释,对这个问题,我的解释是:
  如果C++标准允许我们的union有构造函数,那么,在进⾏空间分配的时候要不要执⾏这个构造函数呢?如果答案是yes,那么如果TestUnion 的构造函数中包含了⼀些内存分配操作,或者其它对整个application状态的修改,那么,如果我今后要⽤到obj的话,事情可能还⽐较合理,但是如果我根本就不使⽤obj这个成员呢?由于obj的引⼊造成的对系统状态的修改显然是不合理的;反之,如果答案是no,那么⼀旦我们今后选中了obj来进⾏操作,则所有信息都没有初始化(如果是普通的struct,没什么问题,但是,如果有虚函数呢?)。更进⼀步,假设现在我们的union不是只有⼀个 TestUnion obj,还有⼀个TestUnion2 obj2,⼆者均有构造函数,并且都在构造函数中执⾏了⼀些内存分配的⼯作(甚⾄⼲了很多其它事情),那么,如果先构造obj,后构造obj2,则执⾏的结果⼏乎可以肯定会造成内存的泄漏。
  鉴于以上诸多⿇烦(可能还有更多⿇烦),在构造union时,编译器只负责分配空间,⽽不负责去执⾏附加的初始化⼯作,为了简化⼯作,只要我们提供了构造函数,就会收到上⾯的error。
同理,除了不能加构造函数,析构函数/拷贝构造函数/赋值运算符也是不可以加。
  此外,如果我们的类中包含了任何virtual函数,编译时,我们将收到如下的错误信息:
  error C2621: union '__unnamed' : member 'obj' has copy constructor
  所以,打消在union中包含有构造函数/析构函数/拷贝构造函数/赋值运算符/虚函数的类成员变量的念头,⽼⽼实实⽤你的C风格struct吧!
  不过,定义普通的成员函数是OK的,因为这不会使得class与C风格的struct有任何本质区别,你完全可以将这样的class理解为⼀个C风格的struct + n个全局函数。
  现在,再看看在类中包含内部union时会有什么不同。看看下⾯的程序,并请注意阅读程序提⽰:
class TestUnion
{
    union DataUnion
    {
      DataUnion(const char*);
      DataUnion(long);
      const char* ch_;
      long l_;
    } data_;
  public:
    TestUnion(const char* ch);
    TestUnion(long l);
};
TestUnion::TestUnion(const char* ch) : data_(ch) // if you want to use initialzing list to initiate a                                nested-union member, the union must not be anonymous and                              must have a constructor。{}
TestUnion::TestUnion(long l) : data_(l)
{}
TestUnion::DataUnion::DataUnion(const char* ch) : ch_(ch)
{}
TestUnion::DataUnion::DataUnion(long l) : l_(l)
{}
int main (void)
{
    return 0;
}
  正如上⾯程序所⽰,C++中的union也可以包含构造函数,但是,这虽然被语⾔所⽀持,但实在是⼀种不佳的编程习惯,因此,我不打算对上⾯的程序进⾏过多的说明。我更推荐如下的编程风格:
class TestUnion
{
    union DataUnion
    {
      const char* ch_;
      long l_;
    } data_;
  public:
    TestUnion(const char* ch);
    TestUnion(long l);
};
TestUnion::TestUnion(const char* ch)
{
    data_.ch_ = ch;
}
TestUnion::TestUnion(long l)
    data_.l_ = l;
}
int main (void)
{
    return 0;
}
它完全是C风格的。
所以,接受这个结论吧:
请按照C中的convention去使⽤union,尽量不要尝试使⽤任何C++附加特性。
union是个好东西,union是个struct,⾥⾯所有成员共享⼀块内存,⼤⼩由size最⼤的member决定,存取成员的时候会以成员的类型来解析这块内存;在gamedev中,union可以在这些⽅⾯有所作为:
1. 换名:
struct Rename
{
public:
union
{
struct
{
float x,y,z,w;
};
struct
{
float vec[4];
};
};
};
这样我们既可以根据具体的含义来访问变量,也可以象数组⼀样的loop;
2 .压缩:
struct Compression
{
public:
bool operator==(const Compression& arg) const { return value == arg.value; }
union
{
struct
{
char a,b,c,d,e,f,g;
};
struct
{
long long value;
};
};
};
这样对于集中处理的情况,⽐如==,就会⼤幅度提⾼效率,象在64位机上,只要⼀次,或者传输数据的情况,压缩解压缩都⾮常⽅便;
3. 危险:
匿名的union⽤法,不是standard,所以在compiler上要确认==>编译器移植性不好;
不同的机器操作系统上数据的size都是不⼀样,表⽰不⼀样,那么在⽤union的时候,尤其是在移植的时候,都是危险的情况;
但是如果系统,compiler都是⼀样的话,在合适的地⽅使⽤union还是可以的。
联合(union)在C/C++⾥⾯见得并不多,但是在⼀些对内存要求特别严格的地⽅,联合⼜是频繁出现,那么究竟什么是联合、怎么去⽤、有什么需要注意的地⽅呢?就这些问题,我试着做⼀些简单的回答,⾥⾯肯定还有不当的地⽅,欢迎指出!
1、什么是联合?
“联合”是⼀种特殊的类,也是⼀种构造类型的数据结构。在⼀个“联合”内可以定义多种不同的数据类型,⼀个被说明为该“联合”类型的变量中,允许装⼊该“联合”所定义的任何⼀种数据,这些数据共享同⼀段内存,已达到节省空间的⽬的(还有⼀个节省空间的类型:位域)。这是⼀个⾮常特殊的地⽅,也是联合的特征。另外,同struct⼀样,联合默认访问权限也是公有的,并且,也具有成员函数。
2、联合与结构的区别?
“联合”与“结构”有⼀些相似之处。但两者有本质上的不同。在结构中各成员有各⾃的内存空间,⼀个结构变量的总长度是各成员长度之和(空结构除外,同时不考虑边界调整)。⽽在“联合”中,各成员共享⼀段内存空间,⼀个联合变量的长度等于各成员中最长的长度。应该说明的是,这⾥所谓的共享不是指把多个成员同时装⼊⼀个联合变量内,⽽是指该联合变量可被赋予任⼀成员值,但每次只能赋⼀种值,赋⼊新值则冲去旧值。
下⾯举⼀个例了来加对深联合的理解。
例4:
#include <stdio.h>
void main()
{
union number
{ /*定义⼀个联合*/
int i;
struct
{ /*在联合中定义⼀个结构*/
char first;
char second;
}half;
}num;
num.i=0x4241; /*联合成员赋值*/
printf("%c%c\n", num.half.first, num.half.second);
num.half.first='a'; /*联合中结构成员赋值*/
num.half.second='b';
printf("%x\n", num.i);
getchar();
}
输出结果为:
AB
从上例结果可以看出: 当给i赋值后, 其低⼋位也就是first和second的值; 当给first和second赋字符后, 这两个字符的ASCII码也将作为i 的低⼋位和⾼⼋位。
3、如何定义?
例如:
union test
{
test() { }
int office;
char teacher[5];
};
定义了⼀个名为test的联合类型,它含有两个成员,⼀个为整型,成员名office;另⼀个为字符数组,数组名为teacher。联合定义之后,即可进⾏联合变量说明,被说明为test类型的变量,可以存放整型量office或存放字符数组teacher。
4、如何说明?
联合变量的说明有三种形式:先定义再说明、定义同时说明和直接说明。
以test类型为例,说明如下:
1)
union test
{
int office;
char teacher[5];
};
union test a,b; /*说明a,b为test类型*/
2)
union test
{
int office;
char teacher[5];
} a,b;
3)
union
{
int office;
char teacher[5];
} a,b;
经说明后的a,b变量均为test类型。a,b变量的长度应等于test的成员中最长的长度,即等于teacher数组的长度,共5个字节。a,b变量如赋予整型值时,只使⽤了4个字节,⽽赋予字符数组时,可⽤5个字节。
5、如何使⽤?
对联合变量的赋值,使⽤都只能是对变量的成员进⾏。联合变量的成员表⽰为:
联合变量名.成员名
例如,a被说明为test类型的变量之后,可使⽤a.class、a.office
不允许只⽤联合变量名作赋值或其它操作,也不允许对联合变量作初始化赋值,赋值只能在程序中进⾏。
还要再强调说明的是,⼀个联合变量,每次只能赋予⼀个成员值。换句话说,⼀个联合变量的值就是联合变员的某⼀个成员值。
6、匿名联合
匿名联合仅仅通知编译器它的成员变量共同享⼀个地址,⽽变量本⾝是直接引⽤的,不使⽤通常的点号运算符语法.
例如:
#include <iostream>
void main()
{
union{
int test;
char c;
};
test=5;
c='a';
std::cout<<i<<" "<<c;
}
正如所见到的,联合成分象声明的普通局部变量那样被引⽤,事实上对于程序⽽⾔,这也正是使⽤这些变量的⽅式.另外,尽管被定义在⼀个联合声明中,他们与同⼀个程序快那的任何其他局部变量具有相同的作⽤域级别.这意味这匿名联合内的成员的名称不能与同⼀个作⽤域内的其他⼀直标志符冲突.
对匿名联合还存在如下限制:
因为匿名联合不使⽤点运算符,所以包含在匿名联合内的元素必须是数据,不允许有成员函数,也不能包含私有或受保护的成员。还有,全局匿名联合必须是静态(static)的,否则就必须放在匿名名字空间中。
7、⼏点需要讨论的地⽅:
1)联合⾥⾯那些东西不能存放?
我们知道,联合⾥⾯的东西共享内存,所以静态、引⽤都不能⽤,因为他们不可能共享内存。
2)类可以放⼊联合吗?
我们先看⼀个例⼦:
class Test
{
public:
union是什么类型
Test():data(0) { }
private:
int data;
};
typedef union _test
{
Test test;
}UI;
编译通不过,为什么呢?
因为联合⾥不允许存放带有构造函数、析够函数、复制拷贝操作符等的类,因为他们共享内存,编译器⽆法保证这些对象不被破坏,也⽆法保证离开时调⽤析够函数。
3)⼜是匿名惹的祸??
我们先看下⼀段代码:
class test
{
public:
test(const char* p);
test(int in);
const operator char*() const {return
data.ch;}
operator long() const {return data.l;}
private:
enum type {Int, String };
union
{
const char* ch;
int i;
}datatype;
type stype;
test(test&);
test& operator=(const test&);
};
test::test(const char *p):stype
(String),datatype.ch(p) { }
test::test(int in):stype(Int),datatype.l(i) {
}
看出什么问题了吗?呵呵,编译通不过。为什么呢?难道datatype.ch(p)和datatype.l(i)有问题吗?
哈哈,问题在哪呢?让我们来看看构造test对象时发⽣了什么,当创建test对象时,⾃然要调⽤其相应的构造函数,在构造函数中当然要调⽤其成员的构造函数,所以其要去调⽤datatype 成员的构造函数,但是他没有构造函数可调⽤,所以出
错。
注意了,这⾥可并不是匿名联合!因为它后⾯紧跟了个data!
4)如何有效的防⽌访问出错?
使⽤联合可以节省内存空间,但是也有⼀定的风险:通过⼀个不适当的数据成员获取当前对象的值!例如上⾯的ch、i交错访问。
为了防⽌这样的错误,我们必须定义⼀个额外的对象,来跟踪当前被存储在联合中的值得类型,我们称这个额外的对象为:union的判别式。
⼀个⽐较好的经验是,在处理作为类成员的union对象时,为所有union数据类型提供⼀组访问函数。
希望本⽂所述对⼤家的C++程序设计有所帮助。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。