1. 程式人生 > >對union的進一步認識與一些深層應用

對union的進一步認識與一些深層應用

雖然平時在程式開發時較少使用union,雖然當初學C語言時,union一章被老師略過沒有介紹,雖然,自認為自己對union的認識已經足夠,但是,在寫完上一篇文章<(大衛的閱讀筆記)C++中使用union的幾點思考>之後,網上的討論驅使我對這一基本的語言特性又投入了一些精力去關注,並寫成了此文.

下面以MSDN中關於union的概述作為開頭,這好像有些無聊,不過,有時候看specification的東西可以給我們很多提示,當我們從應用的角度去關注一樣東西的時候,很多更深層次的考慮被我們忽略了.所以,看下去,裡面有些東西可能正是你忽略了的.

union
union
 [tag] { member-list } [declarators];

[
union] tag declarators;

The union keyword declares a union type and/or a variable of a union type.

A union is a user-defined data type that can hold values of different types at different times. It is similar to a structure
except that all of its members start at the same location in memory. A union variable can contain only one of its members at
a time.
 The size of the union is at least the size of the largest member(大衛注:我想不出來大於的情況).

For related information, see class, struct, and Anonymous Union.

Declaring a Union

Begin the declaration of a union with the union keyword, and enclose the member list in curly braces:

union
 UNKNOWN    // Declare union type
{
   char
   ch;
   int
    i;
   long
   l;
   float
  f;
   double
 d;
}
 var1;          // Optional declaration of union variable
Using a Union

A C++ union is a limited form of the class type. It can contain access specifiers (public, protected, private), member data,
and
 member functions, including constructors and destructors. It cannot contain virtual functions or static data members. It
cannot be used as a base class, nor can it have base classes. Default access of members in a union is public.

A C union type can contain only data members.

In C, you must use the union keyword to declare a union variable. In C++, the union keyword is unnecessary:

Example 1

union
 UNKNOWN var2;   // C declaration of a union variable
UNKNOWN var3;         // C++ declaration of a union variable
Example 2

A variable of a union type can hold one value of any type declared in the union. Use the member-selection operator (.) to access a member of a union:

var1.i = 6;           // Use variable as integer
var2.d = 5.327;       // Use variable as double

為了避免對上述文字有稍許的歪曲,我故意沒有翻譯它,但在此對其進行一些歸納:
1.
union是一種特殊的struct/class,是一種可用於容納多種型別的型別,但與struct/class不同的是,所有的成員變數共享同一儲存空間(最大的那一個成員型別的大小),這使得它具有多變的特性,可以在不同成員中任意切換,而無需藉助強制型別轉換,但這也使得你不能把它當作一個成員變數進行修改而不影響到另一成員變數;
2.
union也可以有構造/解構函式,也可以包含訪問識別符號,但不能包含虛擬函式或靜態成員變數/方法.

關於使用union時需要注意的一些問題,可以參考我的前一篇文章:<(大衛的閱讀筆記)C++中使用union的幾點思考>.
下面談談一些比較有意思並且有意義的union的應用.
1.
in_addr

struct
 in_addr {
  union
 {
          struct
 { u_char s_b1,s_b2,s_b3,s_b4; }   S_un_b;
          struct
 { u_short s_w1,s_w2; }            S_un_w;
          u_long                                   S_addr;
  }
 S_un;
};


對於上面的struct,寫過socket應用的人,肯定都用過它.不知你注意過沒有,它包含了一個很有趣的union,union的各成員具有相同的大小,分別代表同一資訊的不同表現形式.你在進行程式設計的時候也可以利用這一特性來提供同一資訊的不同表現形式,不過要注意,在進行跨平臺應用時,位元組順序的影響可能給你造成一些不必要的麻煩.

2.
匿名union
匿名union是沒有名稱和宣告列表的union,這跟'__unnamed' union不是一回事,它的宣告形式如下:
union
 { member-list } ;

匿名union僅僅通知編譯器它的成員變數共享一個地址,而變數本身是直接引用的,不使用通常的點號運算子語法.也正因此,匿名union與同一程式塊內的其它變數具有相同的作用域級別,需注意命名衝突.
請看下面的例子:

#include <iostream.h>

struct
 DataForm
{

    enum
 DataType { CharData = 1, IntData, StringData };
    DataType type;

    // Declare an anonymous union.
    union
    {

        char
  chCharMem;
        char
 *szStrMem;
        int
   iIntMem;
    };

    void
 print();
};


void
 DataForm::print()
{

    // Based on the type of the data, print the
    // appropriate data type.
    switch( type )
    {

    case
 CharData:
        cout << chCharMem;
        break
;
    case
 IntData:
        cout << szStrMem;
        break
;
    case
 StringData:
        cout << iIntMem;
        break
;
    }
}


此外,匿名union還具有以下約束:
1
).因為匿名聯合不使用點運算子,所以包含在匿名聯合內的元素必須是資料,不允許有成員函式,也不能包含私有或受保護的成員;
2
).全域性匿名聯合必須是靜態(static)的,否則就必須放在匿名名字空間中.

附註:
對匿名union的概念,你或許有些陌生,但對於Windows應用的開發人員,有一個經常用到的結構中就包含了匿名union,它就是VARIANT,也許你沒有注意它罷了:

typedef struct
 FARSTRUCT tagVARIANT VARIANT;
typedef struct
 FARSTRUCT tagVARIANT VARIANTARG;

typedef struct
 tagVARIANT  {
   VARTYPE vt;
   unsigned short
 wReserved1;
   unsigned short
 wReserved2;
   unsigned short
 wReserved3;
   union
 {
      Byte                    bVal;                 // VT_UI1.
      Short                   iVal;                 // VT_I2.
      long                    lVal;                 // VT_I4.
      float                   fltVal;               // VT_R4.
      // ...
   };
};


3.
利用union進行型別轉換
前面已經說過,union具有多變的特性,可以在不同成員中任意切換,而無需藉助強制型別轉換,下面舉例說明這一點(其實1已經很好地說明了這一點):

#include <iostream>
using namespace std;

struct
 DATA
{

    char
 c1;
    char
 c2;
};


int
 main()
{

    union
 { 
        int
 i; 
        DATA data
    }
 _ut;
    
    _ut.i = 0x6162;

    cout << "_ut.data.c1 = " << _ut.data.c1 << endl
        <<
 "_ut.data.c2 = " << _ut.data.c2 << endl;
    
    return
 0;
}


需要提醒你的是,資料型別的轉換,並非union的專長,只是一個可資利用的特性而已.因為,採用union進行型別間轉換極易受平臺影響,如上面的程式採用Intel x86 + Windows 2000 + VC6時輸出為:
_ut.data.c1 = b
_ut.data.c2 = a
(
:因為Intel CPU的架構是Little Endian)
而在Sun的Sparc上,你得到的結果卻是:
_ut.data.c1 = 
_ut.data.c2 = 
(
:因為採用Big Endian時,前兩個位元組為0x0000)

而即便是在同一平臺上,在integer型別與real型別間進行轉換時也不要採用union,否則,你會得到令你莫名其妙的結論(這是由於CPU對real型別的處理方式引起的,該方式在各平臺上有極大區別,同時,根據C++ Standard,這種作法會引起"undefined behavior").

關於利用引用進行型別轉換,可參考<引用在強制型別轉化中的應用>.