woaidongmao

文章均收录自他人博客,但不喜标题前加-[转贴],因其丑陋,见谅!~
随笔 - 1469, 文章 - 0, 评论 - 661, 引用 - 0
数据加载中……

C++中使用union的几点思考

大卫注:
这段时间整理旧资料,看到一些文章,虽然讲的都是些小问题,不大可能用到,但也算是一个知识点,特整理出来与大家共享.与此相关的那篇文章的作者的有些理解是错误的,我写此文,也是纠正为了作者的一些错误认识.当然,如果我的理解有任何错误,也恳请大家批评指正.

C++
虽说被B.S.称作一门新语言,但它毕竟与C有着千丝万缕的联系,虽然B.S.一再坚持,但我还是愿意把C++看作是C ++.
我们应该按照C中的convention去使用union,这是我这篇文章要给出的观点.虽然C++使得我们可以扩展一些新的东西进去,但是,我建议你不要那样去做,看完这篇文章之后,我想你大概也是这么想的.

C
由于没有类的概念,所有类型其实都可以看作是基本类型的组合,因此在union中包含struct也就是一件很自然的事情了,到了C++之后,既然普遍认为C++中的structclass基本等价,那么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,因为这不会使得classC风格的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++附加特性.

posted on 2004-11-12 09:08 大卫的思维空间 阅读(8069) 评论(16)  编辑 收藏

# "比较权威的解释", B.S权威不?可惜你不看他的书。

以下摘自《 the c++ programming language 3rd 》,对为什么union不支持构造函数和析构函数,推荐怎么使用unions都有说明。

10.4.12
Unions [class.union]
A named union is defined as a struct, where every member has the same address (see §C.8.2). A
union can have member functions but not static members.
In general, a compiler cannot know what member of a union is used; that is, the type of the
object stored in a union is unknown. Consequently, a union may not have members with constructors
or destructors. It wouldn’t be possible to protect that object against corruption or to guarantee
that the right destructor is called when the union goes out of scope.
Unions are best used in lowlevel
code, or as part of the implementation of classes that keep
track of what is stored in the union (see §10.6[20]).

其实正如在vckbase/c++论坛上我对你说的,想想union的用途就可以了。union的出现是为了解决一组不定类型数据的空间紧张问题,它是为省空间用的,不是为了封装数据和方法。所以我觉得大卫兄太过于刨根究底了,而忽略了C++的实用性(实用性是C/C++的首要设计思想)

2004-11-12 10:18 | 一笑

# re: (大卫的阅读笔记)C++中使用union的几点思考

谢谢笑兄给出的CPL中的相关论述.我现在主要关注的内容并非C++(虽然C++是我最喜欢的语言),我在blog上发布这些文章也只是希望谈一下自己对于某些语言特性或新技术的认识,以期对后来者有些许帮助,同时,也希望在与大家的交流中提高自己.
^_^,
也许我真的有点刨根究底,不过笑兄的关于"union是为省空间用的"的观点我不认同,虽然,我也看到过类似的说法,但是,union怎么可能节省空间呢?:
union uT
{
int i;
char c;
};
当他被作为char使用时,明明浪费了空间.
只是从另一角度出发:把它当成一种特殊的struct,struct相比时才可以勉强说是节省了空间.
我认为union的引入,更多地是提供了一种解决多种型别共同使用同一空间(这与节省空间是两回事)的机制,借助这种机制,我们可以提供更丰富的语言表现力.
为此,我决定再写一篇文章,谈一些比较有意思/有意义的union的使用,请关注 & 批评指正.

2004-11-13 05:11 | Bill David

# union的进一步认识与一些深层应用[TrackBack]

Ping Back来自:blog.csdn.net
Bill David
引用了该文章,地址:http://blog.csdn.net/billdavid/archive/2004/11/26/195006.aspx

2004-11-26 18:58 | Bill David

# re: (大卫的阅读笔记)C++中使用union的几点思考

呵呵,仁者见仁,智者见智。
但是union的确应该是节省空间使用的。当时的初衷是这样的。但是后来就不一定了。3ks 二位。

2005-04-19 17:38 | 漫天飞舞

# 今日经论坛匿名大侠指点,union还有一个重要的特性

union不支持继承。

特记录于此。

2005-04-19 17:59 | Bill David

# re: union不支持继承。

我突然觉得如果union设计为可继承的也未尝不可。
union base
{
    int mem1;
    char mem2;
};
这个假设被原实现者设计好了,突然,某天我想在base的基础上添加一些特性,于是我写
union derive : base
{
    short mem 3;
};
或许可能实际意义不大吧!但,如果这么说,我reinterpret_cast成员mem1呢?union的实际意义又有多大呢?

不知道你怎么看?

2006-05-16 13:35 | 清风雨

# re: union的几点思考

又看了你的另一篇union的,可能我比较懒惰的缘故,我只得到一个结论: union仅仅只是为了方便。

2006-05-16 13:49 | 清风雨

# re: (大卫的阅读笔记)C++中使用union的几点思考

这都是些什么?

2006-05-18 21:35 | 一二三

# re: (大卫的阅读笔记)C++中使用union的几点思考

看不懂

2006-05-18 21:36 | 一二三

# re: (大卫的阅读笔记)C++中使用union的几点思考

union
{
    struct
           {
                string name;
           }info;
    int  age;
}information;

上面的语句可能出错,has copy constructor.   难道只有把string -->char *??

2006-07-07 15:38 | Tmin

# re: (大卫的阅读笔记)C++中使用union的几点思考

看了文章和回帖,我非常同意一笑兄的看法。我觉得作者没有完全理解节省空间的指的是如何节省,例如我们获取一些信息时,需要把信息数据填充到一个结构中,但是我们需要的信息的数据的类型会根据情况不同而不同,如果我们为了通用的话,可能就需要为所有可能出现的类型都预留相应的空间来存储,但是每次只能使用其中一个有效。其他预留无效。这显然是浪费空间的。所以union使得不同类型数据存放在内存的同一个位置,当然这个空间的大小是能容纳所有类型的数据的。因为unicon的数据类型是运行时决定的,事先并不知道,所以为union做一个构造函数是没有意义的。

2006-10-17 11:48 | hoodlum

# re: (大卫的阅读笔记)C++中使用union的几点思考

刚才回的仓促,可能有论述不是很恰当。但是看了作者的另一篇文章里面引用的英文,论述的也和我本来的想法是一致的。也就是说,可以使用union.memberData来访问成员,但是应该注意的是,使用时只有其中一个成员才是有意义的。union占用的空间大小是所有成员数据大小的最小公倍数。(因此,转为托管代码中就可以使用一个能够容纳union大小的数据类型来代替,或者用通用的字节数组)

例如下面的例子(来自Windows API):
union
{    
   DWORD dwOemId;    
   struct {
                 WORD wProcessorArchitecture; 
                 WORD wReserved; 
              };
  };

它通常占用4个字节,因此可以使用托管代码中的Int32来代替。
其中dwOemId是为了向前兼容某些系统版本,当前已经弃用。这样,一个程序对API的调用可以在不同版本的系统中运转良好,处理各自自己需要的数据。

 

posted on 2008-11-21 22:07 肥仔 阅读(2203) 评论(1)  编辑 收藏 引用 所属分类: C++ 基础

评论

# re: C++中使用union的几点思考  回复  更多评论   

# re: (大卫的阅读笔记)C++中使用union的几点思考
看了文章和回帖,我非常同意一笑兄的看法。我觉得作者没有完全理解“节省空间”的指的是如何节省,例如我们获取一些信息时,需要把信息数据填充到一个结构中,但是我们需要的信息的数据的类型会根据情况不同而不同,如果我们为了通用的话,可能就需要为所有可能出现的类型都预留相应的空间来存储,但是每次只能使用其中一个有效。其他预留无效。这显然是浪费空间的。所以union使得不同类型数据存放在内存的同一个位置,当然这个空间的大小是能容纳所有类型的数据的。因为unicon的数据类型是运行时决定的,事先并不知道,所以为union做一个构造函数是没有意义的。

2006-10-17 11:48 | hoodlum
------------------------------------------------
个人觉得hoodlum说得非常好!
2010-05-11 10:55 | Coastline

只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理