聯合(union)在C/C++裡面見得並不多,但是在一些對記憶體要求特別嚴格的地方,聯合又是頻繁出現,那麼究竟什麼是聯合、怎麼去用、有什麼需要注意的地方呢?就這些問題,我試著做一些簡單的回答,裡面肯定還有不當的地方,歡迎指出!
1、什麼是聯合?
“聯合”是一種特殊的類,也是一種構造類型的資料結構。 在一個“聯合”內可以定義多種不同的資料類型, 一個被說明為該“聯合”類型的變數中,允許裝入該“聯合”所定義的任何一種資料,這些資料共用同一段記憶體,已達到節省空間的目的(還有一個節省空間的類型:位域)。 這是一個非常特殊的地方,也是聯合的特徵。另外,同struct一樣,聯合預設存取權限也是公有的,並且,也具有成員函數。
2、聯合與結構的區別?
“聯合”與“結構”有一些相似之處。但兩者有本質上的不同。在結構中各成員有各自的記憶體空間, 一個結構變數的總長度是各成員長度之和(空結構除外,同時不考慮邊界調整)。而在“聯合”中,各成員共用一段記憶體空間, 一個聯合變數的長度等於各成員中最長的長度。應該說明的是, 這裡所謂的共用不是指把多個成員同時裝入一個聯合變數內, 而是指該聯合變數可被賦予任一成員值,但每次只能賦一種值, 賦入新值則衝去舊值。
3、如何定義?
例如: union test
{
test() { }
int office;
char teacher[5];
};
定義了一個名為test的等位型別,它含有兩個成員,一個為整型,成員名為office;另一個為字元數組,數組名為teacher。聯合定義之後,即可進行聯合變數說明,被說明為test類型的變數,可以存放整型量office或存放字元數組teacher。
4、如何說明?
聯合變數的說明有三種形式:先定義再說明、定義同時說明和直接說明。
以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個位元組。
5、如何使用?
對聯合變數的賦值,使用都只能是對變數的成員進行。
聯合變數的成員表示為:聯合變數名.成員名
例如,a被說明為test類型的變數之後,可使用a.class、a.office
不允許只用聯合變數名作賦值或其它操作,也不允許對聯合變數作初始化賦值,賦值只能在程式中進行。
還要再強調說明的是,一個聯合變數,每次只能賦予一個成員值。換句話說,一個聯合變數的值就是聯合變員的某一個成員值。
6、匿名聯合
匿名聯合僅僅通知編譯器它的成員變數共同享一個地址,而變數本身是直接引用的,不使用通常的點號運算子文法.例如:
#include <iostream>
void main()
{
union{
int test;
char c;
};
test=5;
c=''a'';
std::cout<<i<<" "<<c;
}
正如所見到的,聯合成分象聲明的普通局部變數那樣被引用,事實上對於程式而言,這也正是使用這些變數的方式.另外,儘管被定義在一個聯合聲明中,他們與同一個程式快那的任何其他局部變數具有相同的範圍層級.這意味這匿名聯合內的成員的名稱不能與同一個範圍內的其他一直標誌符衝突.
對匿名聯合還存在如下限制:
因為匿名聯合不使用點運算子,所以包含在匿名聯合內的元素必須是資料,不允許有成員函數,也不能包含私人或受保護的成員。還有,全域匿名聯合必須是靜態(static)的,否則就必須放在匿名名字空間中。
7、幾點需要討論的地方:
1、聯合裡面那些東西不能存放?
我們知道,聯合裡面的東西共用記憶體,所以靜態、引用都不能用,因為他們不可能共用記憶體。
2、類可以放入聯合嗎?
我們先看一個例子:
class Test
{
public:
Test():data(0) { }
private:
int data;
};
typedef union _test
{
Test test;
}UI;
編譯通不過,為什麼呢?
因為聯合裡不允許存放帶有建構函式、析夠函數、複製拷貝操作符等的類,因為他們共用記憶體,編譯器無法保證這些對象不被破壞,也無法保證離開時調用析夠函數。
8、又是匿名惹的禍??
我們先看下一段代碼:
1 class test
2 {
3 public:
4 test(const char* p);
5 test(int in);
6 const operator char*() const {return data.ch;}
7 operator long() const {return data.l;}
8 private:
9 enum type {Int, String };
10 union
11 {
12 const char* ch;
13 int i;
14 } datatype;
15 type stype;
16 test(test&);
17 test& operator=(const test&);
18 };
19 test::test(const char *p):stype(String),datatype.ch(p) { }
20 test::test(int in):stype(Int),datatype.l(i) { }
21
看出什麼問題了嗎?呵呵,編譯通不過。為什麼呢?難道datatype.ch(p)和datatype.l(i)有問題嗎?
哈哈,問題在哪呢?讓我們來看看構造test對象時發生了什麼,當建立test對象時,自然要調用其相應的建構函式,在建構函式中當然要調用其成員的建構函式,所以其要去調用datatype成員的建構函式,但是他沒有建構函式可調用,所以出錯。
注意了,這裡可並不是匿名聯合!因為它後面緊跟了個data!
9、如何有效防止訪問出錯?
使用聯合可以節省記憶體空間,但是也有一定的風險:通過一個不適當的資料成員擷取當前對象的值!例如上面的ch、i交錯訪問。
為了防止這樣的錯誤,我們必須定義一個額外的對象,來跟蹤當前被儲存在聯合中的值得類型,我們稱這個額外的對象為:union的判別式。
一個比較好的經驗是,在處理作為類成員的union對象時,為所有union資料類型提供一組訪問函數。
轉載自:http://ideage.javaeye.com/blog/210614