联合(union)在C/C++里面见得并很少,可是在一些对内存要求特别严格的地方,联合又是频繁出现,那么究竟什么是联合、怎么去用、有什么须要注意的地方呢?就这些问题,我试着作一些简单的回答,里面确定还有不当的地方,欢迎指出!
一、什么是联合?
“联合”是一种特殊的类,也是一种构造类型的数据结构。在一个“联合”内能够定义多种不一样的数据类型, 一个被说明为该“联合”类型的变量中,容许装入该“联合”所定义的任何一种数据,这些数据共享同一段内存,已达到节省空间的目的(还有一个节省空间的类型:位域)。 这是一个很是特殊的地方,也是联合的特征。另外,同struct同样,联合默认访问权限也是公有的,而且,也具备成员函数。
二、联合与结构的区别?
“联合”与“结构”有一些类似之处。但二者有本质上的不一样。在结构中各成员有各自的内存空间, 一个结构变量的总长度是各成员长度之和(空结构除外,同时不考虑边界调整)。而在“联合”中,各成员共享一段内存空间, 一个联合变量的长度等于各成员中最长的长度。应该说明的是, 这里所谓的共享不是指把多个成员同时装入一个联合变量内, 而是指该联合变量可被赋予任一成员值,但每次只能赋一种值, 赋入新值则冲去旧值。
下面举一个例了来加对深联合的理解。
例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
6261
从上例结果能够看出: 当给i赋值后, 其低八位也就是first和second的值; 当给first和second赋字符后, 这两个字符的ASCII码也将做为i 的低八位和高八位。
三、如何定义?
例如:
union test
{
test() { }
int office;
char teacher[5];
};
定义了一个名为test的联合类型,它含有两个成员,一个为整型,成员名office;另外一个为字符数组,数组名为teacher。联合定义以后,便可进行联合变量说明,被说明为test类型的变量,能够存放整型量office或存放字符数组teacher。
四、如何说明?
联合变量的说明有三种形式:先定义再说明、定义同时说明和直接说明。
以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个字节。
五、如何使用?
对联合变量的赋值,使用都只能是对变量的成员进行。联合变量的成员表示为:
联合变量名.成员名
例如,a被说明为test类型的变量以后,可以使用a.class、a.office
不容许只用联合变量名做赋值或其它操做,也不容许对联合变量做初始化赋值,赋值只能在程序中进行。
还要再强调说明的是,一个联合变量,每次只能赋予一个成员值。换句话说,一个联合变量的值就是联合变员的某一个成员值。
六、匿名联合
匿名联合仅仅通知编译器它的成员变量共同享一个地址,而变量自己是直接引用的,不使用一般的点号运算符语法.例如:
#i nclude <iostream>
void main()
{
union{
int test;
char c;
};
test=5;
c=′a′;
std::cout<<i<<" "<<c;
}
正如所见到的,联合成分象声明的普通局部变量那样被引用,事实上对于程序而言,这也正是使用这些变量的方式.另外,尽管被定义在一个联合声明中,他们与同一个程序快那的任何其余局部变量具备相同的做用域级别.这意味这匿名联合内的成员的名称不能与同一个做用域内的其余一直标志符冲突.
对匿名联合还存在以下限制:
由于匿名联合不使用点运算符,因此包含在匿名联合内的元素必须是数据,不容许有成员函数,也不能包含私有或受保护的成员。还有,全局匿名联合必须是静态(static)的,不然就必须放在匿名名字空间中。
七、几点须要讨论的地方:
一、联合里面那些东西不能存放?
咱们知道,联合里面的东西共享内存,因此静态、引用都不能用,由于他们不可能共享内存。
二、类能够放入联合吗?
咱们先看一个例子:
class Test
{
public:
Test():data(0) { }
private:
int data;
};
typedef union _test
{
Test test;
}UI;
编译通不过,为何呢?
由于联合里不容许存放带有构造函数、析够函数、复制拷贝操做符等的类,由于他们共享内存,编译器没法保证这些对象不被破坏,也没法保证离开时调用析够函数。
三、又是匿名惹的祸??
咱们先看下一段代码:
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! 四、如何有效的防止访问出错? 使用联合能够节省内存空间,可是也有必定的风险:经过一个不适当的数据成员获取当前对象的值!例如上面的ch、i交错访问。 为了防止这样的错误,咱们必须定义一个额外的对象,来跟踪当前被存储在联合中的值得类型,咱们称这个额外的对象为:union的判别式。 一个比较好的经验是,在处理做为类成员的union对象时,为全部union数据类型提供一组访问函数。