复合资料型态(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
$