C++中使用union的几点思考(转)
C++中使用union的几点思考
大卫注:
这段时间整理旧资料,看到一些文章,虽然讲的都是些小问题,不大可能用到,但也算是一个知识点,特整理出来与大家共享.与此相关的那篇文章的作者的有些理解是错误的,我写此文,也是纠正为了作者的一些错误认识.当然,如果我的理解有任何错误,也恳请大家批评指正.
C++虽说被B.S.称作一门新语言,但它毕竟与C有着千丝万缕的联系,虽然B.S.一再坚持,但我还是愿意把C++看作是C ++.
我们应该按照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++附加特性.
C++中使用union的几点思考(转)的更多相关文章
- Ms SQLServer中的Union和Union All的使用方法和区别
Ms SQLServer中的Union和Union All的使用方法和区别 SQL UNION 操作符 UNION 操作符用于合并两个或多个 SELECT 语句的结果集. 请注意,UNION 内部的 ...
- Oracle中的Union、Union All、Intersect、Minus
Oracle中的Union.Union All.Intersect.Minus 众所周知的几个结果集集合操作命令,今天详细地测试了一下,发现一些问题,记录备考. 假设我们有一个表Student,包括 ...
- golang 中的 sizeof 以及 golang中的 union
golang 中的 sizeof: 1: int(unsafe.Sizeof(uint32(0))) 2: int(reflect.TypeOf(uint32(0)).Size()) golang中的 ...
- Boost--variant (C++中的union)
union联合体类型的问题 只能用于内部类型,这使得union在C++中几乎没有用 所以boost提供了variant,相当于是C++中的union #include "boost/vari ...
- mysql中的union操作(整理)
mysql中的union操作(整理) 一.总结 一句话总结: union两侧的字段数和字段类型要是一样的 union可以接多个 orderby和排序可以在最后的union组合之后 1.union简单实 ...
- OpenGL shader 中关于顶点坐标值的思考
今天工作中需要做一个事情: 在shader内部做一些空间距离上的计算,而且需要对所有的点进行计算,符合条件的显示,不符合条件的点不显示. 思路很简单,在vertex shader内知道顶点坐标,进行计 ...
- 由项目中一个hash2int函数引发的思考
hash2int /** * 计算一个字符串的md5折算成int返回 * @param type $str * @return type */ function hash2int($str) { $m ...
- SQL语句中:UNION与UNION ALL的区别
有些人看到题目,瞬间觉得楼主也太弱了吧,这种问题也要拿出来写,这种问题 随便会点sql 的人基本都会 Union 是会删除冗余数据 Union ALL 不会删除冗余数据 将所有的结果都展现给用户 ...
- C#Linq中的Union All/Union/Intersect和Top/Bottom和Paging和SqlMethods,skip,take,takewhile,skipwhile,编译查询等
我们继续讲解LINQ to SQL语句,这篇我们来讨论Union All/Union/Intersect操作和Top/Bottom操作和Paging操作和SqlMethods操作 . Union Al ...
随机推荐
- ios实现类似魔兽小地图功能 在
写了一个类似魔兽小地图功能的控件. 比如你有一个可以放大缩小的scrollView.会在里面进行一些放大缩小,点击里面的按钮呀,等操作. 这个小地图控件.就会和你的大scrollView同步.并有缩略 ...
- hdu 3635 Dragon Balls(加权并查集)2010 ACM-ICPC Multi-University Training Contest(19)
这道题说,在很久很久以前,有一个故事.故事的名字叫龙珠.后来,龙珠不知道出了什么问题,从7个变成了n个. 在悟空所在的国家里有n个城市,每个城市有1个龙珠,第i个城市有第i个龙珠. 然后,每经过一段时 ...
- TabHost Tab的添加和删除
TabHost 添加Tab项: tabhost = this.getTabHost(); TabSpec tabSpec = tabhost.newTabSpec("news"); ...
- 自定义控件:抽屉SlidingDrawer——wrap_content非全屏
android:allowSingleTap 指示抽屉是否可以打开/通过手柄上的一个水龙头关闭. android:animateOnClick 表示所述抽屉是否应该打开/与当用户点击手柄动画关闭 ...
- <译>Selenium Python Bindings 5 - Waits
如今,大多数的Web应用程序使用AJAX技术.当页面加载到浏览器,页面中的元素也许在不同的时间间隔内加载.这使得元素很难定位,如果在DOM中的元素没有呈现,它将抛出ElementNotVisibleE ...
- Python 代码性能优化技巧
选择了脚本语言就要忍受其速度,这句话在某种程度上说明了 python 作为脚本的一个不足之处,那就是执行效率和性能不够理想,特别是在 performance 较差的机器上,因此有必要进行一定的代码优化 ...
- 用Python操作Mysql
平时的主要编程语言是Java,开发时也主要用Mysql,经常为了测试,调试的目的需要操作数据库,比如备份,插入测试数据,修改测试数据,有些时候不能简单的用SQL就能完成任务,或都很好的完成任务,用Ja ...
- Python多线程和Python的锁
Python多线程 Python中实现多线程有两种方式,一种基于_thread模块(在Python2.x版本中为thread模块,没有下划线)的start_new_thread()函数,另一种基于th ...
- VS2015创建的C++程序在Debug模式下不能调试
如题,不能调试的问题,解决办法: 1.修改工程属性页 2.修改VS2015托管兼容模式 工具->选项->调试,勾选使用托管兼容
- Linux Oracle服务启动&停止脚本与开机自启动
在CentOS 6.3下安装完Oracle 10g R2,重开机之后,你会发现Oracle没有自行启动,这是正常的,因为在Linux下安装Oracle的确不会自行启动,必须要自行设定相关参数,首先先介 ...