1、什么是联合?
“联合”是一种特殊的类,也是一种构造类型的数据结构。在一个“联合”内可以定义多种不同的数据类型, 一个被说明为该“联合”类型的变量中,允许装入该“联合”所定义的任何一种数据,这些数据共享同一段内存,已达到节省空间的目的(还有一个节省空间的类型:位域)。 这是一个非常特殊的地方,也是联合的特征。另外,同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
还要再强调说明的是,一个联合变量,每次只能赋予一个成员值。换句话说,一个联合变量的值就是联合变员的某一个成员值。
匿名联合仅仅通知编译器它的成员变量共同享一个地址,而变量本身是直接引用的,不使用通常的点号运算符语法.例如:
#include <iostream>
void main()
{
union{
int test;
char c;
};
test=5;
c=′a′;
std::cout<<i<<" "<<c;
}
正如所见到的,联合成分象声明的普通局部变量那样被引用,事实上对于程序而言,这也正是使用这些变量的方式.另外,尽管被定义在一个联合声明中,他们与同一个程序快那的任何其他局部变量具有相同的作用域级别.这意味这匿名联合内的成员的名称不能与同一个作用域内的其他一直标志符冲突.
对匿名联合还存在如下限制:
因为匿名联合不使用点运算符,所以包含在匿名联合内的元素必须是数据,不允许有成员函数,也不能包含私有或受保护的成员。还有,全局匿名联合必须是静态(static)的,否则就必须放在匿名名字空间中。
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) {
}
哈哈,问题在哪呢?让我们来看看构造test对象时发生了什么,当创建test对象时,自然要调用其相应的构造函数,在构造函数中当然要调用其成员的构造函数,所以其要去调用datatype成员的构造函数,但是他没有构造函数可调用,所以出
注意了,这里可并不是匿名联合!因为它后面紧跟了个data!
4、如何有效的防止访问出错?
使用联合可以节省内存空间,但是也有一定的风险:通过一个不适当的数据成员获取当前对象的值!例如上面的ch、i交错访问。
为了防止这样的错误,我们必须定义一个额外的对象,来跟踪当前被存储在联合中的值得类型,我们称这个额外的对象为:union的判别式。
一个比较好的经验是,在处理作为类成员的union对象时,为所有union数据类型提供一组访问函数。
c++中union的应用剖析
熟悉c的程序员都知道union(联合体)的用法,利用union可以用相同的存储空间存储不同型别的数据类型,从而节省内存空间。当访问其内成员时可用"."和"->"来直接访问。在c++出现后,它继承了union并保留了其在c中的特性。但是在c++中的union又有了新的扩展,这需要大家了解,要不然你会感到费解和迷惑。下面我讲两点。
一、在union中存储对象
在c中union中可以存储任意类型的内置数据类型,那么在c++中union是否可以存储对象呢?还是让我们看一个例子吧,这比任何言语都能说明问题,不是吗?
#pragma warning(disable : 4786)
#include
using namespace std;
class testunion
{
public:
testunion(long l):data_(l)
{
};
int data_;
};
typedef union _tagutype_
{
testunion obj;
}ut;
int main (void)
{
return 0;
}
这样不行,union中不可以存储testunion类的对象,但在c中union可以存储struct呀,为什么不能存储类的对象呢?很简单,请问,在c中union可以存储带有构造函数的struct吗?对了,在c中的struct是没有构造函数的。所以如果c++中union可以存储有构造函数的类的对象就不太符合逻辑,那不是说c++和c完全兼容吗?不错,正因为这一点,c++中union不可以存储有构造函数的类的对象,但是可以存储不带构造函数的类的对象,这样就和c保持一致了,不想信你试试。对testunion类的声明进行如下修改:
class testunion
{
public:
int data_;
};
<span style="font-size:18px; font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> 再进行编译,一切ok!。但是这样却失去了c++的构造初始化特性,这样做是没有任何意义的,我只是在说其在c++中的语义,并不是推荐大家使用(绝对不推荐)。但是我们可以在union中存储对象的指针,从而引用不同的对象类型。不用我再多说了吧,大家还是试试吧!</span>
二、类中union的初始化
由于union的共享内存特点,我们可以使我们的类存储不同的型别而不浪费内存空间,在类中我们可以声明一个union存储不同型别的指针,示例如下:
#pragma warning(disable : 4786)
#include
using namespace std;
class testunion
{
enum storetype{long,const_charp};
union
{
const char* ch_;
long l_;
} data_;
storetype stype_;
testunion(testunion&);
testunion& operator=(const testunion&);
public:
testunion(const char* ch);
testunion(long l);
operator const char*() const {return data_.ch_;}
operator long() const {return data_.l_;}
};
testunion::testunion(const char* ch):data_.ch_(ch),stype_(const_charp)
{
}
testunion::testunion(long l):data_.l_(l),stype_(long)
{
}
int main (void)
{
testunion pszobj("yuankai");
testunion lobj(1234);
cout<(pszobj)< cout<
return 0;
}
真是不幸,编译都通不过,好象没有什么问题呀,为什么呢?data_.ch_(ch)和data_.l_(l)有问题吗?如果你问一个c程序员他会告诉你,绝对没问题。你不会去怀疑编译器有问题吧!不好意思!我一开始就是这么想的,真是惭愧。费解,迷惑。让我们来看看构造testunion对象时发生了什么,这样你就会明白了。当创建testunion对象时,自然要调用其相应的构造函数,在构造函数中当然要调用其成员的构造函数,所以其要去调用union成员的构造函数,但是其为匿名的,有没有构造函数可调用,所以出错。很明显在c++中union和class一样它可以有构造函数,不能如此直接引用其成员。struct同样有这限制。只要我们给其定义一个构造函数什么问题都解决了。示例如下:
class testunion
{
enum storetype{long,const_charp};
union dataunion //不能匿名
{
dataunion(const char*); //声明const char*构造函数
dataunion(long); //声明long构造函数
const char* ch_;
long l_;
} data_;
storetype stype_;
testunion(testunion&);
testunion& operator=(const testunion&);
public:
testunion(const char* ch);
testunion(long l);
operator const char*() const {return data_.ch_;}
operator long() const {return data_.l_;}
};
testunion::testunion(const char* ch):data_(ch),stype_(const_charp)
{//注意data_(ch),这里直接引用data_
}
testunion::testunion(long l):data_(l),stype_(long)
{//注意data_(l),这里直接引用data_
}
testunion::dataunion::dataunion(const char* ch):ch_(ch)
{
}
testunion::dataunion::dataunion(long l):l_(l)
{
}