C++之复合资料型态 第一部(参考 列举 指标)

复合资料型态(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都可以。

接下来我们印出aa_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_ptrint *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; // NULL
    std::cout << "p: " << p << std::endl;
    
    return 0;
}

编译后执行,结果如下

$ g++ u0704_4.cpp
$./a.out 复制代码
人数:22
p: 0x7fff503acae8
p: 0x0
$

  • 28
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值