其他資料型別

類型別名 (typedef / using)

類型別名是可用於標識某個型別的另一個名稱。在 C++ 中,任何有效的型別都可以被別名化,以便用不同的識別符號來引用它。

在 C++ 中,有兩種語法可以建立此類類型別名:第一種繼承自 C 語言,使用 typedef 關鍵字。

typedef 現有型別 新型別名 ;

其中,existing_type 是任何型別,無論是基本型別還是複合型別,new_type_name 是賦予該型別的新名稱的識別符號。

例如:

1
2
3
4
typedef char C;
typedef unsigned int WORD;
typedef char * pChar;
typedef char field [50];

這定義了四個類型別名:CWORDpCharfield,它們分別是 charunsigned intchar*char[50] 的別名。一旦定義了這些別名,它們就可以在任何宣告中使用,就像任何其他有效型別一樣。

1
2
3
4
C mychar, anotherchar, *ptc1;
WORD myword;
pChar ptc2;
field name;

最近,C++ 語言引入了第二種定義類型別名的語法。

1
using new_type_name = existing_type ;

例如,與上面相同的類型別名可以定義為:

1
2
3
4
using C = char;
using WORD = unsigned int;
using pChar = char *;
using field = char [50];

typedef 定義的別名和用 using 定義的別名在語義上是等價的。唯一的區別是 typedef 在模板領域有某些限制,而 using 沒有。因此,using 更通用,儘管 typedef 歷史更長,並且在現有程式碼中可能更常見。

請注意,typedefusing 都不會建立新的、獨立的資料型別。它們只為現有型別建立同義詞。這意味著上面用 WORD 型別宣告的 myword,其型別也可以被認為是 unsigned int;這並不重要,因為兩者實際上都指向同一個型別。

類型別名可用於縮短冗長或令人困惑的型別名稱,但它們作為將程式從其使用的底層型別中抽象出來的工具最為有用。例如,透過使用 int 的別名來指代特定型別的引數,而不是直接使用 int,可以在將來的版本中輕鬆地將該型別替換為 long(或其他型別),而無需更改每個使用它的地方。

聯合 (Unions)

聯合允許記憶體的同一部分作為不同的資料型別進行訪問。其宣告和使用方式與結構體相似,但功能完全不同。


union type_name {
  member_type1 member_name1;
  member_type2 member_name2;
  member_type3 member_name3;
  .
  .
} object_names;


這將建立一個新的、由 type_name 標識的聯合型別,其中所有成員元素都佔用記憶體中相同的物理空間。此型別的大小是其最大成員元素的大小。例如:

1
2
3
4
5
union mytypes_t {
  char c;
  int i;
  float f;
} mytypes;

聲明瞭一個物件(mytypes),它有三個成員:

1
2
3
mytypes.c
mytypes.i
mytypes.

這些成員中的每一個都是不同的資料型別。但由於它們都指向記憶體中的同一位置,因此修改其中一個成員將影響所有成員的值。無法在它們中儲存不同的值,並讓每個值都與其他值相互獨立。

聯合的用途之一是能夠以整體形式或作為由更小元素組成的陣列或結構體來訪問一個值。例如:

1
2
3
4
5
6
7
8
union mix_t {
  int l;
  struct {
    short hi;
    short lo;
    } s;
  char c[4];
} mix;

如果我們假設執行該程式的系統 int 型別的大小為 4 位元組,short 型別的大小為 2 位元組,那麼上面定義的聯合允許訪問同一組 4 位元組:mix.lmix.smix.c,我們可以根據我們想要如何訪問這些位元組來使用它們:就像它們是 int 型別的單個值,或者像它們是兩個 short 型別的值,或者分別是一個 char 元素的陣列。該示例在聯合中混合了型別、陣列和結構體,以演示訪問資料的不同方式。對於小端系統,這個聯合可以表示為:


聯合成員在記憶體中的確切對齊方式和順序取決於系統,這可能導致可移植性問題。

匿名聯合 (Anonymous unions)

當聯合是類(或結構體)的成員時,它們可以被宣告為沒有名稱。在這種情況下,它們成為匿名聯合,其成員可以直接透過成員名從物件訪問。例如,請看以下兩個結構體宣告之間的區別:

帶有常規聯合的結構體帶有匿名聯合的結構體
struct book1_t {
char title[50];
char author[50];
union {
float dollars;
int yen;
} price;
} book1;
struct book2_t {
char title[50];
char author[50];
union {
float dollars;
int yen;
};
} book2;

這兩種型別之間唯一的區別是,在第一種型別中,成員聯合有一個名稱(price),而在第二種型別中則沒有。這影響了訪問此型別物件的成員 dollarsyen 的方式。對於第一種型別的物件(帶有常規聯合),訪問方式為:

1
2
book1.price.dollars
book1.price.

而對於第二種型別的物件(帶有匿名聯合),訪問方式為:

1
2
book2.dollars
book2.

再次提醒,因為它是一個成員聯合(而不是成員結構體),成員 dollarsyen 實際上共享相同的記憶體位置,因此它們不能用於同時儲存兩個不同的值。price 可以用 dollarsyen 設定,但不能同時使用兩者。

列舉型別 (enum)

列舉型別是透過一組自定義識別符號(稱為列舉成員)作為可能值來定義的型別。這些列舉型別的物件可以取任何這些列舉成員作為其值。

其語法是:


enum type_name {
  value1,
  value2,
  value3,
  .
  .
} object_names;


這將建立一個名為 type_name 的型別,它可以取 value1value2value3 等中的任何一個作為值。該型別的物件(變數)可以直接例項化為 object_names

例如,可以定義一種名為 colors_t 的新變數型別來儲存顏色,其宣告如下:

1
enum colors_t {black, blue, green, cyan, red, purple, yellow, white};

注意,此宣告的定義中不包含任何其他型別,無論是基本型別還是複合型別。換句話說,這在某種程度上是憑空建立了一個全新的資料型別,而沒有基於任何其他現有型別。這種新型別 color_t 的變數可能取的值是在大括號內列出的列舉成員。例如,一旦聲明瞭 colors_t 列舉型別,以下表達式將是有效的:

1
2
3
4
colors_t mycolor;
 
mycolor = blue;
if (mycolor == green) mycolor = red;

enum 宣告的列舉型別的值可以隱式轉換為整數型別。事實上,這樣的 enum 的元素在內部總是被賦予一個等效的整數數值,並且它們可以被隱式轉換為該數值。如果沒有另外指定,第一個可能值對應的整數值為 0,第二個為 1,第三個為 2,依此類推……因此,在上面定義的 colors_t 資料型別中,black 等效於 0blue 等效於 1green 等效於 2,依此類推……

可以為列舉型別中的任何可能值指定一個特定的整數值。如果它後面的常量值本身沒有被賦予自己的值,則它自動被假定為前一個值加一。例如:

1
2
3
enum months_t { january=1, february, march, april,
                may, june, july, august,
                september, october, november, december} y2k;

在這種情況下,列舉型別 months_t 的變數 y2k 可以包含從 januarydecember 的 12 個可能值中的任何一個,這些值等效於 112 之間的整數值(而不是 011,因為 january 已被設為等於 1)。

帶 enum class 的列舉型別

但是,在 C++ 中,可以建立真正的 enum 型別,它們既不能隱式轉換為 int,其列舉成員值也不是 int 型別,而是 enum 型別本身,從而保持了型別安全。它們使用 enum class(或 enum struct)而不是簡單的 enum 來宣告。

1
enum class Colors {black, blue, green, cyan, red, purple, yellow, white};

enum class 型別的每個列舉成員值都需要限定在其型別作用域內(這實際上也適用於 enum 型別,但只是可選的)。例如:

1
2
3
4
Colors mycolor;
 
mycolor = Colors::blue;
if (mycolor == Colors::green) mycolor = Colors::red;

enum class 宣告的列舉型別對其底層型別也有更多的控制;它可以是任何整型資料型別,如 charshortunsigned int,這主要用於確定該型別的大小。這透過在列舉型別名稱後加冒號和底層型別來指定。例如:

1
enum class EyeColor : char {blue, green, brown};

這裡,Eyecolor 是一個獨立的型別,其大小與 char 相同(1 位元組)。
Index
目錄