復合資料型態(compound type) 是由其他資料型態(data type) 定義出來的型態, C++ 中的復合資料型態包括參考(reference) 、列舉(enumeration) 、陣列(array) 、指標(pointer ) 、結構(structure) 及聯合(union) 。
參考
參考是變數(variable) 的別名(alias) ,例如
#include <iostream>int main() {int a = 22;int& a_ref = a;std::cout << "a: " << a << std::endl;std::cout << "a_ref: "<< a_ref << std::endl;a_ref = 11;std::cout << "a: " << a << std::endl;std::cout << "a_ref: " << a_ref << std::endl;return 0;
}
? 第5 行,宣告參考的型態(type) 必須與參考所指向的變數型態相同,然后在型態名稱后后使用& 運算子(operator) 標明這是個參考變數,因此這個例子的參考變數為a_ref,等號右邊為所要指向的變數,此例為a ?
int& a_ref = a;
由于C++ 是自由格式的程式語言,因此寫成int & a_ref或int &a_ref都可以。
接下來我們印出a與a_ref的值,然后把a_ref改成11
a_ref = 11;
這樣a也會變成11,編譯后執行結果如下
$ g++ u0701_1.cpp
$./a.out 復制代碼
答:22
參考編號:22
答:11
參考編號: 11
$
由上可看出參考等同于原來的變數,這被稱為左值參考(lvalue reference) 。 C++11 增加了一個右值參考(rvalue reference) ,用以增進運算的效率,這是用&&來宣告,例如
int&& ref = a + b + c;
右值參考是對運算式的參考,舉例如下
#include <iostream>int main() {int a = 22;int b = 33;int c = 11;int&& ref = a + b + c;std::cout << "a: " << a << std::endl;std::cout << "b: " << b << std::endl;std::cout << "c: " << c<< std::endl;std::cout << "ref: " << ref << std::endl;ref += 66;std::cout << "a: " << a << std::endl;std::cout << "b: "<< b << std::endl;std::cout << "c: " << c << std::endl;std::cout << "ref: " << ref << std::endl;return 0;
}
編譯后執行,結果如下
$ g++ u0701_2.cpp -std=c++0x
$./a.out 復制代碼
答:22
乙:33
額: 11
參考:66
答:22
乙:33
額: 11
參考:132
$
列舉
列舉是一組整數常數,例如
#include <iostream>int main() {enum Day {Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday};std::cout << "Sunday: " << Sunday << std::endl;std::cout << "Monday: " << Monday << std::endl;std::cout << "Tuesday: " << Tuesday << std::endl;std::cout << "Wednesday: " << Wednesday << std::endl;std::cout << "Thursday: " << Thursday << std::endl;std::cout << "Friday: " << Friday << std::endl;std::cout << "Saturday: " << Saturday << std::endl;Day today = Wednesday;std::cout << today << std::endl;return 0;
}
第4 行,定義一個列舉型態Day,使用關鍵字(keyword) enum,后面接著型態名稱Day,然后大括弧中是列舉的識別字(identifier) ,這被稱為非作用域列舉(unscoped enumeration )
enum Day {Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday};
列舉常數為從0開始遞增的整數常數數列,因此第30 行宣告的today亦為整數常數,Wednesday是第4 個值,所以是整數3
Day today = Wednesday;
編譯執行,結果如下
$ g++ u0702_1.cpp
$./a.out 復制代碼
周日:0
星期一:1
星期二:2
星期三:3
星期四:4
周五:5
星期六:6
今天:3
$
列舉也可以匿名(anonymous) 與指定起始整數,例如
#include <iostream>int main() {enum {apple, banana = 11, orange, peach = 5};std::cout << "apple: " << apple << std::endl;std::cout << "banana: " << banana << std::endl;std::cout << "orange: " << orange << std::endl;std::cout << "peach: " << peach << std::endl;return 0;
}
此例的列舉沒有識別字,另外將banana設定為11,因此orange就由11遞增為12,最后的peach則設定為5
enum {apple, banana = 11, orange, peach = 5};
編譯后執行,結果如下
$ g++ u0702_2.cpp
$./a.out 復制代碼
蘋果:0
香蕉:11
橙色:12
桃子:5
$
列舉也可以跟struct或class一起宣告,形成作用域列舉(scoped enumeration) ,例如
enum class Color {RED, GREEN, BLUE};
C++11 中,列舉常數可以改用其他型態,此時要在列舉識別字后面加上冒號及型態名稱,舉例如下
#include <iostream>enum class Color: char {RED = 'r',GREEN = 'g',BLUE = 'b',
};int main() {Color r;r = Color::RED;Color g;g = Color::GREEN;Color b;b = Color::BLUE;std::cout << "RED: " << static_cast<char>(r) << std::endl;std::cout << "GREEN: " << static_cast<char>(g) << std::endl;std::cout << "BLUE: " << static_cast<char>(b) << std::endl;return 0;
}
這里定義一個作用域列舉,并且將列舉常數的型態指定為char
enum class Color: char {RED = 'r',GREEN = 'g',BLUE = 'b',
};
編譯后執行,結果如下
$ g++ u0702_3.cpp -std=c++0x
$./a.out 復制代碼
紅色:r
綠色:g
藍色:b
$
指標
指標是儲存記憶體位址(address) 的資料型態,例如
#include <iostream>int main() {int a = 22;int* a_ptr = &a;std::cout << "a_ptr: " << a_ptr << std::endl;std::cout << "*a_ptr: " << *a_ptr << std::endl;return 0;
}
第5 行,宣告指標的型態,必須與指標所指向的變數型態相同,然后在型態名稱后使用* 運算子標明這是個指標變數,因此這個例子的參考變數為a_ptr,等號右邊為所要指向的變數,此例為a,a之前的&則是取址運算子(address-of operator)
int* a_ptr = &a;
由于C++ 是自由格式的程式語言,因此寫成int?* a_ptr或int?*a_ptr都可以。
接下來先印出a_ptr的值,然后利用反參考運算子(dereference operator)?*取得a_ptr所指向變數的值
std::cout << "a_ptr: " << a_ptr << std::endl;
std::cout << "*a_ptr: " << *a_ptr << std::endl;
編譯后執行,結果如下
$ g++ u0704_1.cpp
$./a.out 復制代碼
a_ptr:0x7fff50b81b08
*a_ptr: 22
$
注意,編譯器(compiler) 會依據運算子出現的位置判斷運算子的用途,例如
int* a_ptr; // 定義a_ptr為指標變數
int& a_ref; // 宣告 a_ref 為參考變數
a_ptr = &a; // & 為取地址侵犯子,取得一個的記憶體位址
*a_pr = 36; // * 為反參考進攻子,將設定為36
陣列識別字其實就是個指標,另外指標也可以作算術運算,例如
#include <iostream>int main() {int a[] = {1, 2, 3, 4, 5};std::cout << "a[2]: " << *(a + 2) << std::endl;std::cout << "a[4]: " << *(a + 4) << std::endl;return 0;
}
這里用陣列名稱與反參考運算子取得元素,指標的算術運算如同陣列的索引值,由于陣列名稱為第1 個元素索引值為0的記憶體位址,所以加2就是索引值為2的元素記憶體位址,也就是第3 個元素,加4就是索引值為4的元素記憶體位址,也就是第5 個元素
std::cout << "a[2]: " << *(a + 2) << std::endl;
std::cout << "a[4]: " << *(a + 4) << std::endl;
編譯后執行,結果如下
$ g++ u0704_2.cpp
$./a.out 復制代碼
a[2]: 3
a[4]: 5
$
事實上,所有指標都預設能隱性轉換指向void,舉例如下
#include <iostream>int main() {int n = 1;int* p = &n;void* p2 = p;int* p3 = static_cast<int*>(p2);std::cout << "n: " << n << std::endl;std::cout << "p: " << p << std::endl;std::cout << "*p3: " << *p3 << std::endl;return 0;
}
這里將指向int的指標重新指派給指向void的指標,轉換回來要利用關鍵字 static_cast
int* p = &n;
void* p2 = p;
int* p3 = static_cast<int*>(p2);
編譯后執行,結果如下
$ g++ u0704_3.cpp
$./a.out 復制代碼
數量:1
p: 0x7fff55f89b18
*p3:1
$
C++11 新增一個關鍵字nullptr表示空的指標,等同于巨集NULL,舉例如下
#include <iostream>int main() {int n = 22;std::cout << "n: " << n << std::endl;int* p = &n;std::cout << "p: " << p << std::endl;p = nullptr; // NULLstd::cout << "p: " << p << std::endl;return 0;
}
編譯后執行,結果如下
$ g++ u0704_4.cpp
$./a.out 復制代碼
人數:22
p: 0x7fff503acae8
p: 0x0
$