C++联合体union用法实例详解_c++语言union和struct的运用例题-程序员宅基地

技术标签: C/C++  

转载自:

http://www.jb51.net/article/66711.htm

本文实例讲述了C++联合体union用法。分享给大家供大家参考。具体如下:

我们应该按照C中的convention去使用union,这是我这篇文章要给出的观点。虽然C++使得我们可以扩展一些新的东西进去,但是,我建议你不要那样去做,看完这篇文章之后,我想你大概也是这么想的。

  C由于没有类的概念,所有类型其实都可以看作是基本类型的组合,因此在union中包含struct也就是一件很自然的事情了,到了C++之后,既然普遍认为C++中的struct与class基本等价,那么union中是否可以有类成员呢?先来看看如下的代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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时会有什么不同。看看下面的程序,并请注意阅读程序提示:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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也可以包含构造函数,但是,这虽然被语言所支持,但实在是一种不佳的编程习惯,因此,        我不打算对上面的程序进行过多的说明。我更推荐如下的编程风格:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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. 换名:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct Rename
{
   public :
     union
     {
       struct
       {
         float x,y,z,w;
       };
       struct
       {
         float vec[4];
       };
     };
};

 这样我们既可以根据具体的含义来访问变量,也可以象数组一样的loop;

 2 .压缩:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#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
6261

从上例结果可以看出: 当给i赋值后, 其低八位也就是first和second的值; 当给first和second赋字符后, 这两个字符的ASCII码也将作为i 的低八位和高八位。

3、如何定义?

例如:

?
1
2
3
4
5
6
union test
{
   test() { }
   int office;
   char teacher[5];
};

定义了一个名为test的联合类型,它含有两个成员,一个为整型,成员名office;另一个为字符数组,数组名为teacher。联合定义之后,即可进行联合变量说明,被说明为test类型的变量,可以存放整型量office或存放字符数组teacher。

4、如何说明?

联合变量的说明有三种形式:先定义再说明、定义同时说明和直接说明。

以test类型为例,说明如下:
1)

?
1
2
3
4
5
6
union test
{
   int office;
   char teacher[5];
};
union test a,b; /*说明a,b为test类型*/

2)

?
1
2
3
4
5
union test
{
   int office;
   char teacher[5];
} a,b;

3)

?
1
2
3
4
5
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、匿名联合

匿名联合仅仅通知编译器它的成员变量共同享一个地址,而变量本身是直接引用的,不使用通常的点号运算符语法.
例如:

?
1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
void main()
{
   union {
   int test;
   char c;
   };
   test=5;
   c= 'a' ;
   std::cout<<i<< " " <<c;
}

正如所见到的,联合成分象声明的普通局部变量那样被引用,事实上对于程序而言,这也正是使用这些变量的方式.另外,尽管被定义在一个联合声明中,他们与同一个程序快那的任何其他局部变量具有相同的作用域级别.这意味这匿名联合内的成员的名称不能与同一个作用域内的其他一直标志符冲突.
对匿名联合还存在如下限制:
因为匿名联合不使用点运算符,所以包含在匿名联合内的元素必须是数据,不允许有成员函数,也不能包含私有或受保护的成员。还有,全局匿名联合必须是静态(static)的,否则就必须放在匿名名字空间中。

7、几点需要讨论的地方:

1)联合里面那些东西不能存放?

我们知道,联合里面的东西共享内存,所以静态、引用都不能用,因为他们不可能共享内存。

2)类可以放入联合吗?

我们先看一个例子:

?
1
2
3
4
5
6
7
8
9
10
11
class Test
{
   public :
   Test():data(0) { }
   private :
   int data;
};
typedef union _test
{
   Test test;
}UI;

编译通不过,为什么呢?
因为联合里不允许存放带有构造函数、析够函数、复制拷贝操作符等的类,因为他们共享内存,编译器无法保证这些对象不被破坏,也无法保证离开时调用析够函数。

3)又是匿名惹的祸??
我们先看下一段代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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++程序设计有所帮助。


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u010066903/article/details/79321010

智能推荐

分布式光纤传感器的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告_预计2026年中国分布式传感器市场规模有多大-程序员宅基地

文章浏览阅读3.2k次。本文研究全球与中国市场分布式光纤传感器的发展现状及未来发展趋势,分别从生产和消费的角度分析分布式光纤传感器的主要生产地区、主要消费地区以及主要的生产商。重点分析全球与中国市场的主要厂商产品特点、产品规格、不同规格产品的价格、产量、产值及全球和中国市场主要生产商的市场份额。主要生产商包括:FISO TechnologiesBrugg KabelSensor HighwayOmnisensAFL GlobalQinetiQ GroupLockheed MartinOSENSA Innovati_预计2026年中国分布式传感器市场规模有多大

07_08 常用组合逻辑电路结构——为IC设计的延时估计铺垫_基4布斯算法代码-程序员宅基地

文章浏览阅读1.1k次,点赞2次,收藏12次。常用组合逻辑电路结构——为IC设计的延时估计铺垫学习目的:估计模块间的delay,确保写的代码的timing 综合能给到多少HZ,以满足需求!_基4布斯算法代码

OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版-程序员宅基地

文章浏览阅读3.3k次,点赞3次,收藏5次。OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版

关于美国计算机奥赛USACO,你想知道的都在这_usaco可以多次提交吗-程序员宅基地

文章浏览阅读2.2k次。USACO自1992年举办,到目前为止已经举办了27届,目的是为了帮助美国信息学国家队选拔IOI的队员,目前逐渐发展为全球热门的线上赛事,成为美国大学申请条件下,含金量相当高的官方竞赛。USACO的比赛成绩可以助力计算机专业留学,越来越多的学生进入了康奈尔,麻省理工,普林斯顿,哈佛和耶鲁等大学,这些同学的共同点是他们都参加了美国计算机科学竞赛(USACO),并且取得过非常好的成绩。适合参赛人群USACO适合国内在读学生有意向申请美国大学的或者想锻炼自己编程能力的同学,高三学生也可以参加12月的第_usaco可以多次提交吗

MySQL存储过程和自定义函数_mysql自定义函数和存储过程-程序员宅基地

文章浏览阅读394次。1.1 存储程序1.2 创建存储过程1.3 创建自定义函数1.3.1 示例1.4 自定义函数和存储过程的区别1.5 变量的使用1.6 定义条件和处理程序1.6.1 定义条件1.6.1.1 示例1.6.2 定义处理程序1.6.2.1 示例1.7 光标的使用1.7.1 声明光标1.7.2 打开光标1.7.3 使用光标1.7.4 关闭光标1.8 流程控制的使用1.8.1 IF语句1.8.2 CASE语句1.8.3 LOOP语句1.8.4 LEAVE语句1.8.5 ITERATE语句1.8.6 REPEAT语句。_mysql自定义函数和存储过程

半导体基础知识与PN结_本征半导体电流为0-程序员宅基地

文章浏览阅读188次。半导体二极管——集成电路最小组成单元。_本征半导体电流为0

随便推点

【Unity3d Shader】水面和岩浆效果_unity 岩浆shader-程序员宅基地

文章浏览阅读2.8k次,点赞3次,收藏18次。游戏水面特效实现方式太多。咱们这边介绍的是一最简单的UV动画(无顶点位移),整个mesh由4个顶点构成。实现了水面效果(左图),不动代码稍微修改下参数和贴图可以实现岩浆效果(右图)。有要思路是1,uv按时间去做正弦波移动2,在1的基础上加个凹凸图混合uv3,在1、2的基础上加个水流方向4,加上对雾效的支持,如没必要请自行删除雾效代码(把包含fog的几行代码删除)S..._unity 岩浆shader

广义线性模型——Logistic回归模型(1)_广义线性回归模型-程序员宅基地

文章浏览阅读5k次。广义线性模型是线性模型的扩展,它通过连接函数建立响应变量的数学期望值与线性组合的预测变量之间的关系。广义线性模型拟合的形式为:其中g(μY)是条件均值的函数(称为连接函数)。另外,你可放松Y为正态分布的假设,改为Y 服从指数分布族中的一种分布即可。设定好连接函数和概率分布后,便可以通过最大似然估计的多次迭代推导出各参数值。在大部分情况下,线性模型就可以通过一系列连续型或类别型预测变量来预测正态分布的响应变量的工作。但是,有时候我们要进行非正态因变量的分析,例如:(1)类别型.._广义线性回归模型

HTML+CSS大作业 环境网页设计与实现(垃圾分类) web前端开发技术 web课程设计 网页规划与设计_垃圾分类网页设计目标怎么写-程序员宅基地

文章浏览阅读69次。环境保护、 保护地球、 校园环保、垃圾分类、绿色家园、等网站的设计与制作。 总结了一些学生网页制作的经验:一般的网页需要融入以下知识点:div+css布局、浮动、定位、高级css、表格、表单及验证、js轮播图、音频 视频 Flash的应用、ul li、下拉导航栏、鼠标划过效果等知识点,网页的风格主题也很全面:如爱好、风景、校园、美食、动漫、游戏、咖啡、音乐、家乡、电影、名人、商城以及个人主页等主题,学生、新手可参考下方页面的布局和设计和HTML源码(有用点赞△) 一套A+的网_垃圾分类网页设计目标怎么写

C# .Net 发布后,把dll全部放在一个文件夹中,让软件目录更整洁_.net dll 全局目录-程序员宅基地

文章浏览阅读614次,点赞7次,收藏11次。之前找到一个修改 exe 中 DLL地址 的方法, 不太好使,虽然能正确启动, 但无法改变 exe 的工作目录,这就影响了.Net 中很多获取 exe 执行目录来拼接的地址 ( 相对路径 ),比如 wwwroot 和 代码中相对目录还有一些复制到目录的普通文件 等等,它们的地址都会指向原来 exe 的目录, 而不是自定义的 “lib” 目录,根本原因就是没有修改 exe 的工作目录这次来搞一个启动程序,把 .net 的所有东西都放在一个文件夹,在文件夹同级的目录制作一个 exe._.net dll 全局目录

BRIEF特征点描述算法_breif description calculation 特征点-程序员宅基地

文章浏览阅读1.5k次。本文为转载,原博客地址:http://blog.csdn.net/hujingshuang/article/details/46910259简介 BRIEF是2010年的一篇名为《BRIEF:Binary Robust Independent Elementary Features》的文章中提出,BRIEF是对已检测到的特征点进行描述,它是一种二进制编码的描述子,摈弃了利用区域灰度..._breif description calculation 特征点

房屋租赁管理系统的设计和实现,SpringBoot计算机毕业设计论文_基于spring boot的房屋租赁系统论文-程序员宅基地

文章浏览阅读4.1k次,点赞21次,收藏79次。本文是《基于SpringBoot的房屋租赁管理系统》的配套原创说明文档,可以给应届毕业生提供格式撰写参考,也可以给开发类似系统的朋友们提供功能业务设计思路。_基于spring boot的房屋租赁系统论文