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; } 编译该程序,我们将被告知: 而如果去掉那个什么也没干的构造函数,则一切OK。 为什么编译器不允许我们的union成员有构造函数呢?我无法找到关于这个问题的比较权威的解释,对这个问题,我的解释是: 如果C++标准允许我们的union有构造函数,那么,在进行空间分配的时候要不要执行这个构造函数呢?如果答案是yes,那么如果TestUnion 的构造函数中包含了一些内存分配操作,或者其它对整个application状态的修改,那么,如果我今后要用到obj的话,事情可能还比较合理,但是如果我根本就不使用obj这个成员呢?由于obj的引入造成的对系统状态的修改显然是不合理的;反之,如果答案是no,那么一旦我们今后选中了obj来进行 操作,则所有信息都没有初始化(如果是普通的struct,没什么问题,但是,如果有虚函数呢?)。更进一步,假设现在我们的union不是只有一个 TestUnion obj,还有一个TestUnion2 obj2,二者均有构造函数,并且都在构造函数中执行了一些内存分配的工作(甚至干了很多其它事情),那么,如果先构造obj,后构造obj2,则执行的 结果几乎可以肯定会造成内存的泄漏。 鉴于以上诸多麻烦(可能还有更多麻烦),在构造union时,编译器只负责分配空间,而不负责去执行附加的初始化工作,为了简化工作,只要我们提供了构造函数,就会收到上面的error。 同理,除了不能加构造函数,析构函数/拷贝构造函数/赋值运算符也是不可以加。 此外,如果我们的类中包含了任何virtual函数,编译时,我们将收到如下的错误信息: 所以,打消在union中包含有构造函数/析构函数/拷贝构造函数/赋值运算符/虚函数的类成员变量的念头,老老实实用你的C风格struct吧! 现在,再看看在类中包含内部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位机上,只要一次,或者传输数据的情况,压缩解压缩都非常方便; 联合(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%cn",num.half.first,num.half.second); num.half.first='a'; /*联合中结构成员赋值*/ num.half.second='b'; printf("%xn",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类型为例,说明如下: 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、如何使用? 对联合变量的赋值,使用都只能是对变量的成员进行。联合变量的成员表示为: 6、匿名联合 匿名联合仅仅通知编译器它的成员变量共同享一个地址,而变量本身是直接引用的,不使用通常的点号运算符语法. #include <iostream> void main() { union{ int test; char c; }; test=5; c='a'; std::cout<<i<<" "<<c; } 正如所见到的,联合成分象声明的普通局部变量那样被引用,事实上对于程序而言,这也正是使用这些变量的方式.另外,尽管被定义在一个联合声明中,他们与同一个程序快那的任何其他局部变量具有相同的作用域级别.这意味这匿名联合内的成员的名称不能与同一个作用域内的其他一直标志符冲突. 7、几点需要讨论的地方: 1)联合里面那些东西不能存放? 我们知道,联合里面的东西共享内存,所以静态、引用都不能用,因为他们不可能共享内存。 2)类可以放入联合吗? 我们先看一个例子: class Test { public: 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)有问题吗? 4)如何有效的防止访问出错? 使用联合可以节省内存空间,但是也有一定的风险:通过一个不适当的数据成员获取当前对象的值!例如上面的ch、i交错访问。 为了防止这样的错误,我们必须定义一个额外的对象,来跟踪当前被存储在联合中的值得类型,我们称这个额外的对象为:union的判别式。 一个比较好的经验是,在处理作为类成员的union对象时,为所有union数据类型提供一组访问函数。 希望本文所述对大家的C++程序设计有所帮助。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- c# – 找不到资源(ASP.NET MVC 5中的错误)
- ruby-on-rails – 使用Ubuntu 11.04,我无法使用CTRL-C关闭r
- XML Schema. Base64binary类型vs String类型
- ruby-on-rails-3 – 我可以在Rails中使用常用的ActiveRecor
- ruby-on-rails – 活动记录关联未定义方法’val'(构建,
- 用正则匹配一串字符串中的ip地址
- c# – 强制在抽象超类的子类中使用属??性
- 高性能IO设计的Reactor和Proactor模式
- Fresco的报错:Binary XML file line #0: Error inflating
- ORACLE 10G修改字符编码没有超字符集的限制