C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12...

125
C++ 程序设计 2章 变量和类型 计算机学院 黄章进 [email protected]

Transcript of C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12...

Page 1: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

C++ 程序设计

第2章变量和类型

计算机学院黄章进

[email protected]

Page 2: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

内容

2.1 基本内置类型

2.2 变量

2.3 复合类型

2.4 const 限定符

2.5 处理类型

2.6 自定义数据结构

变量和类型

2

Page 3: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

数据类型

数据类型(type)是程序的基础

数据的意义

能在数据上执行的操作

C++支持广泛的数据类型

基本内置类型

字符、整型、浮点数

自定义数据类型

C++标准库定义了可变长字符串和向量等

3

Page 4: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

基本内置类型

C++定义的基本数据类型

算术类型(arithmetic type)

字符、整型数、布尔值、浮点数

空类型void

不对应具体的值,常见于函数返回类型

4

Page 5: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

算术类型

算术类型分为两类:

整型(integral type):包括字符和布尔类型在内

浮点型(floating-point type)

算术类型的尺寸(比特数)在不同的机器上有所差别

C++标准仅规定尺寸的最小值,但允许编译器实现采用更大的尺寸

基本内置类型

5

Page 6: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

算术类型

基本内置类型

6

Page 7: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

算术类型

bool类型的取值是true或false

字符类型

基本字符类型char

确保可以存放机器基本字符集中任意字符对应的数字值,即大小和一个机器byte一样

wchar_t、char16_t和char32_t用于扩展字符集,来支持国际化

wchar_t类型用于确保可以存放机器的最大扩展字符集中的任一字符

类型char16_t和char32_t为Unicode字符集服务

基本内置类型

7

Page 8: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

算术类型

其他整型:short <= int <= long <= long long

long long是C++ 11新定义的

浮点型单精度float以1个字(32比特)表示

双精度double以2个字(64比特)表示

扩展精度long double以3或4个字(96或128比特)表示

float和double一般分别有7和16个有效位,long double常用于有特殊精度需求的硬件

基本内置类型

8

Page 9: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

符号类型和无符号类型

除去布尔型和扩展字符型之外,其他整型都可以是带符号的(signed)和无符号的(unsigned)

符号类型可以表示正数、负数或0

int、short、long、long long

无符号类型只能表示大于等于0的值

unsigned或unsigned int、unsigned short、unsigned long、unsigned long long

基本内置类型

9

Page 10: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

符号类型和无符号类型

基本字符类型有三种:char、signed char和unsigned char

char和signed char是不一样的

表现形式只有两种:带符号的和无符号的char等价于signed char还是unsigned char取决于编译器的实现

无符号类型中的所有比特都用来存储值8比特的unsigned char表示[0,255]内的数

C++标准没有规定符号类型应如何表示一般8比特的signed char表示范围为[-128, 127]

基本内置类型

10

Page 11: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

如何选择类型

确定数值不可能为负时,选择无符号类型

使用int执行整数运算 short常常显得太小,而long一般和int有一样的尺寸

如果数值超过了int的表示范围,选用long long

在算术表达式中不要使用char或bool

类型char是否有符号依机器而定

如果需要使用一个不大的整数,明确指定类型为signed char或unsigned char

执行浮点数运算用double

float通常精度不够,双精度和单精度浮点数的计算代价相差无几

long double提供的精度一般没有必要,且计算代价高

基本内置类型

11

Page 12: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

类型转换

对象的类型定义了对象能包含的数据和能执行的操作

当在程序中使用了一种类型而对象应该取另一种类型时,程序会自动执行类型转换

初始化

赋值

算术运算

基本内置类型

12

Page 13: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

类型转换

赋值时的类型转换规则:把非布尔的算术值赋给一个bool对象时,0值转换为

false,非0值转换为true

把bool值赋给非布尔算术对象时,false转换为0,true

转换为1

把浮点数赋给整型对象时,保留小数点前的整数部分

把整数值赋给浮点对象时,小数部分为0

赋给无符号类型一个超出其表示范围的值时,结果是该值对无符号类型表示数值总数取模后的余数

例如,8位的unsigned char可表示0至255区间的256个数,那么用-1赋值后的结果是255

赋给带符号类型一个超出其表示范围的值时,结果是未定义的

基本内置类型

13

Page 14: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

例子

bool b = 42; // b is true

int i = b; // i has value 1

i = 3.14; // i has value 3

double pi = i; // pi has value 3.0

unsigned char c = -1; // assuming 8-bit chars, c has

value 255

signed char c2 = 256; // assuming 8-bit chars, the value

of c2 is undefined

类型转换

14

Page 15: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

含有无符号类型的表达式

当算术表达式中既有unsigned值又有int值时,int值会转换为unsigned类型unsigned u = 10;int i = -42;std::cout << i + i << std::endl; // prints -84std::cout << u + i << std::endl; // if 32-bit ints, prints 4294967264

当从无符号数中减去一个值时,必须确保结果不能是一个负值unsigned u1 = 42, u2 = 10;std::cout << u1 - u2 << std::endl; // ok: result is 32std::cout << u2 - u1 << std::endl; // ok: but the result will wrap around

类型转换

15

Page 16: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

含有无符号类型的表达式

无符号数不会小于0这一事实影响到循环的写法// WRONG:u can never be less than 0; the condition

will always succeed

for (unsigned u = 10; u >= 0; --u)

std::cout << u << std::endl;

假设unsigned 型占32位,u=0时,--u的结果将会是4294967295

类型转换

16

Page 17: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

练习

练习2.3:读程序写结果,32位unsigned表示范围为[0, 4294967296=232)

unsigned u = 10, u2 = 42;

std::cout << u2 - u << std::endl;

std::cout << u - u2 << std::endl;

int i = 10, i2 = 42;

std::cout << i2 - i << std::endl;

std::cout << i - i2 << std::endl;

std::cout << i - u << std::endl;

std::cout << u - i << std::endl;

类型转换

17

Page 18: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

字面值常量

每个字面值常量(literal)对应一种数据类型,由其形式和值决定

整型字面值

浮点型字面值

字符和字符串字面值

布尔字面值

指针字面值

基本内置类型

18

Page 19: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

整型字面值

整型字面值 (integer literal)可写作十进制数、八进制数或十六进制数形式

以0开头的整数代表八进制,以0x或0X开头的代表十六进制数

20 /* 十进制 */ 024 /* 八进制 */ 0x14 /* 十六进制 */

整型字面值的类型由其值和形式决定

默认情况下,十进制字面值是符号数,

八进制和十六进制字面值可能是带符号的也可能是无符号的

字面值常量

19

Page 20: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

整型字面值

十进制字面值的类型是其数值匹配的int、long和long long中尺寸最小的那个

八进制和十六进制字面值的类型是其数值匹配的int、unsigned int、long、unsigned

long和long long和unsigned long long中尺寸最小者

short类型没有对应的字面值

如果一个字面值连与之关联的最大数据类型都存储不了,将产生错误

字面值常量

20

Page 21: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

浮点型字面值

浮点型字面值(floating-point literal)包括小数和以科学计数法表示的指数

科学计数法中,指数部分用E或e标识

3.14159 3.14159E0 0. 0e0 .001

默认的,浮点型字面值为double类型

字面值常量

21

Page 22: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

字符和字符串字面值

'a' // character literal

"a" // string literal"Hello World!" // string literal

char型字面值:由单引号括起来的一个字符

字符串字面值:双引号括起来的零个或多个字符编译器在每个字符串的结尾处添加一个空字符(’\0’)

如果两个字符串字面值位置相邻,且仅由空格、缩进和换行符分隔,则表示一个字符串

// multiline string literalstd::cout << "a really, really long string literal "

"that spans two lines" << std::endl;

字面值常量

22

Page 23: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

转义序列

有两类字符程序中不能直接使用

不可打印字符:退格或控制字符

C++语言中有特殊含义的字符:单引号、双引号、问号、反斜线

需要用反斜线开始的转义序列(escape

sequence)来表示这些字符:newline \n horizontal tab \t alert (bell) \a

vertical tab \v backspace \b double quote \"

backslash \\ question mark \? single quote \'

carriage return \r formfeed \f

字面值常量

23

Page 24: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

转义序列

程序中,转义序列被当作一个字符std::cout << '\n'; // prints a newline

std::cout << "\tHi!\n"; // prints a tab followd by "Hi!"

and a newline

也可以使用广义转义序列,其形式是\x

后跟1个或多个十六进制数字,或\后紧跟1到3个八进制数字

数字部分表示字符对应的数值

\7 (bell) \12 (newline) \40 (blank)

\0 (null) \115 ('M') \x4d ('M')

字面值常量

24

Page 25: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

转义序列

可以像使用普通字符那样使用转义序列std::cout << "Hi \x4dO\115!\n"; // prints Hi MOM!

followed by a newline

std::cout << '\115' << '\n'; // prints M followed by a

newline

如果反斜线\后跟的八进制数字超过3个,则只有前3个数字与\构成转义序列;而\x要用到后面跟的所有十六进制数字"\1234" \x1234

字面值常量

25

Page 26: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

指定字面值的类型

通过添加前缀或后缀,可以改变整型、浮点型和字符型字面值的默认类型

使用长整型字面值时,使用大写字面L来标记,以免小写字母l和数字1混淆

字面值常量

26

Page 27: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

指定字面值的类型

对整型字面值,可以分别指定是否带符号以及尺寸。也可以将U与L或LL组合使用以UL为后缀的字面值的类型根据具体数值取

unsigned long或unsigned long long类型L'a' // wide character literal, type is wchar_tu8"hi!" // utf-8 string literal (utf-8 encodes a Unicode character in 8 bits)42ULL // unsigned integer literal, type is unsigned long long1E-3F // single-precision floating-point literal, type is float3.14159L // extended-precision floating-point literal, type is long double

字面值常量

27

Page 28: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

布尔字面值和指针字面值

true和false是布尔类型的字面值

bool test = false;

nullptr是指针字面值字面值常量

28

Page 29: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

练习

练习2.5:指出下述字面值的类型

(a) 'a', L'a', "a", L"a"

(b) 10, 10u, 10L, 10uL, 012, 0xC

(c) 3.14, 3.14f, 3.14L

(d) 10, 10u, 10., 10e-2

练习2.6:下面两组定义是否有区别?

int month = 9, day = 7;

int month = 09, day = 07;

字面值常量

29

Page 30: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

练习

练习2.7:给出下面字面值的含义和类型,其中\145表示字符e

(a) "Who goes with F\145rgus?\012"

(b) 3.14e1L

(c) 1024f

(d) 3.14L

字面值常量

30

Page 31: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

变量

变量(variable)提供了一个具名的可供程序操作的存储空间对象(object)指具有某种数据类型的内存空间,不严格区分是内置还是类类型也不区分是否命名或是否只读

值(value)一般指只读的数据

C++中的每个变量都有其数据类型数据类型决定着变量所占内存空间的大小和布局方式、该空间能存储值的范围,以及变量能参与的操作

31

Page 32: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

变量定义

变量定义的基本形式是:类型标识符 变量名1,变量名2,…,变量名n;

定义时还可以为一个或多个变量赋初值,初始化变量的值可以是任意复杂的表达式// ok: price is defined and initialized before it is used to initialize discountdouble price = 109.99, discount = price * 0.16;

初始化和赋值是两个完全不同的操作初始化的含义是创建变量时赋予其一个初始值

赋值的含义是把对象的当前值擦除,以一个新值来替代

变量

32

Page 33: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

列表初始化

C++提供了初始化的多种形式int units_sold = 0;int units_sold = {0};int units_sold{0};int units_sold(0);

列表初始化:使用由花括号括起来的一组初始值来进行初始化的形式

用于内置类型的变量时,如果使用列表初始化存在丢失信息的风险,则编译器报错long double ld = 3.1415926536;

int a{ld}, b = {ld}; // error: narrowing conversion required

int c(ld), d = ld; // ok: but value will be truncated

变量定义

33

Page 34: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

默认初始化

如果定义变量时没有指定初值,则变量被默认初始化

如果内置类型的变量未被显式初始化,其值由定义的位置决定定义于任何函数体之外的变量(全局变量)被初始化为0

函数体内部的变量(局部变量)将不被初始化

如果类的对象没有显式初始化,其值由类确定 string类规定如果没有指定初值则生成一个空串std::string empty; // empty implicitly initialized to the empty string

Sales_item item; // default-initialized Sales_item object

变量定义

34

Page 35: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

练习

练习2.9:解释下列定义的含义,对于非法的定义,请说明错在何处并改正(a) std::cin >> int input_value;

(b) int i = { 3.14 };

(c) double salary = wage = 9999.99;

(d) int i = 3.14;

练习2.10:下列变量的初值分别是什么?std::string global_str;

int global_int;

int main()

{

int local_int;

std::string local_str;

}

变量定义

35

Page 36: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

变量声明与定义

声明(declaration)使得名字为程序所知

声明规定了变量的类型和名字

定义(definition)负责创建和名字关联的实体

除了完成声明的功能外,定义还申请存储空间,也可能为变量赋一个初始值

变量能且只能被定义一次,但是可以被声明多次

变量

36

Page 37: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

变量声明与定义

如果想声明一个变量而非定义它,在变量名前添加关键字extern,而且不要显式地初始化变量

extern int i; // declares but does not define i

int j; // declares and defines j

任何包含了显式初始化的声明即成为定义

extern double pi = 3.1416; // definition

在函数体内部,如果试图初始化一个由extern关键字标记的变量,将引发错误

变量

37

Page 38: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

练习

练习2.11:指出下面语句是声明还说定义:(a) extern int ix = 1024;

(b) int iy;

(c) extern int iz;

变量声明与定义

38

Page 39: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

标识符

C++的标识符(identifier)由字母、数字和下划线组成,其中必须以字母或下划线开头标识符的长度没有限制,但对大小写敏感// defines four different int variables

int somename, someName, SomeName, SOMENAME;

不能使用C++的关键字

C++也为标准库保留了一些名字用户定义的标识符不能连续出现两个下划线,也不能以下划线紧接大写字母开头

定义在函数体外的标识符不能以下划线开头

变量

39

Page 40: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

C++关键字

C++操作符替代名:

标识符

40

Page 41: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

变量命名规范

标识符要能体现实际含义

变量名一般用小写字母,如index,不要使用Index或INDEX

用户自定义的类名一般以大写字母开头,如Sales_item

如果标识符由多个单词组成,则单词间应有明显区分,如student_loan或,studentLoan,不要使用studentloan

标识符

41

Page 42: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

变量命名规范

unix like风格:单词用小写字母,每个单词直接用下划线'_'分割,例如text_mutex,kernel_text_address

Windows风格:大小写字母混用,单词连在一起,每个单词首字母大写。不过Windows风格如果遇到大写专有用语时会有些别扭,例如命名一个读取RFC文本的函数,命令为ReadRFCText,看起来就没有unixlike的read_rfc_text清晰了

匈牙利命名法:用这种方法命名的变量显示了其数据类型。匈牙利命名主要包括三个部分:基本类型、一个或更多的前缀、一个限定词

标识符

42

Page 43: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

练习

练习2.12:请指出下面名字中哪些是非法的?

(a) int double = 3.14;

(b) int _;

(c) int catch-22;

(d) int 1_or_2 = 1;

(e) double Double = 3.14;

标识符

43

Page 44: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

名字的作用域

作用域(scope)是程序的一部分,在其中名字有其特定的含义

同一名字在不同作用域中可能指向不同的实体

名字的有效区域开始于名字的声明语句,以声明语句所在的作用域末端为结束

尽量在第一次使用变量时再定义它

变量

44

Page 45: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

例子

#include <iostream>int main(){

int sum = 0;// sum values from 1 through 10 inclusivefor (int val = 1; val <= 10; ++val)

sum += val; // equivalent to sum = sum + valstd::cout << "Sum of 1 to 10 inclusive is "

<< sum << std::endl;return 0;

}

main定义于所有花括号外,具有全局作用域

sum定义main函数体内,具有块作用域

val定义于for语句内,在for语句外不能被访问

名字的作用域

45

Page 46: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

嵌套的作用域

作用域可包含其他作用域,被包含(或被嵌套)的作用域称为内层作用域(inner scope),包含别的作用域的作用域称为外层作用域(outer scope)

内层作用域可以使用外层作用域中声明的名字

允许在内层作用域中重新定义外层作用域中声明的名字

如果函数有可能用到某全局变量,则不宜再定义一个同名的局部变量

名字的作用域

46

Page 47: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

嵌套的作用域

#include <iostream>

// Program for illustration purposes only: It is bad style for a function

// to use a global variable and also define a local variable with the

same name

int reused = 42; // reused has global scope

int main()

{

int unique = 0; // unique has block scope

// output #1: uses global reused; prints 42 0

std::cout << reused << " " << unique << std::endl;

int reused = 0; // new, local object named reused hides global reused

// output #2: uses local reused; prints 0 0

std::cout << reused << " " << unique << std::endl;

// output #3: explicitly requests the global reused; prints 42 0

std::cout << ::reused << " " << unique << std::endl;

return 0;

}

名字的作用域

47

Page 48: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

练习

练习2.13:下面程序中j的值是多少?int i = 42;

int main()

{

int i = 100;

int j = i;

}

练习2.14:下面程序合法吗?输出什么?int i = 100, sum = 0;

for (int i = 0; i != 10; ++i)

sum += i;

std::cout << i << " " << sum << std::endl;

嵌套的作用域

48

Page 49: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

复合类型

声明语句由一个基本类型(base type)和紧随其后的一个声明符(declarator)列表组成

每个声明符命名了一个变量,并指定该变量为与基本类型有关的某种类型

复合类型(compound type)是指基于其他类型定义的类型

引用

指针

49

Page 50: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

引用

引用(reference)为对象取一个别名声明符写成&d的形式,其中d是声明的变量名

定义引用时,必须初始化,绑定到一个已存在的对象

一旦初始化完成,无法令引用绑定到另外一个对象

int ival = 1024;

int &refVal = ival; // refVal refers to (is another name for) ival

int &refVal2; // error: a reference must be initialized

复合类型

50

Page 51: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

引用即别名

对引用的所有操作都是在与之绑定的对象上进行的

为引用赋值,实际上是把值赋给与引用绑定的对象

获取引用的值,实际上是获取与之绑定的对象的值

以引用作为初始值,实际上是以与引用绑定的对象作为初始值

引用本身不是一个对象,因此不能定义引用的引用

引用

51

Page 52: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

引用即别名

int ival = 1024;

int &refVal = ival; // refVal refers to (is another name for) ival

refVal = 2; // assigns 2 to the object to which refValrefers, i.e., to ival

int ii = refVal; // same as ii = ival

// ok: refVal3 is bound to the object to which refVal is bound, i.e., to ival

int &refVal3 = refVal;

// initializes i from the value in the object to which refVal is bound

int i = refVal; // ok: initializes i to the same value as ival

引用

52

Page 53: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

引用的定义

允许在一条语句中定义多个引用,但每个引用标识符都必须以符号&开头int i = 1024, i2 = 2048; // i and i2 are both ints

int &r = i, r2 = i2; // r is a reference bound to i; r2 is

an int

int i3 = 1024, &ri = i3; // i3 is an int; ri is a reference

bound to i3

int &r3 = i3, &r4 = i2; // both r3 and r4 are references

引用

53

Page 54: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

引用的定义

一般地,引用的类型要和与之绑定的对象严格匹配

一般地,引用只能绑定到对象,不能与字面值或表达式的计算结果绑定int &refVal4 = 10; // error: initializer must be an

object

double dval = 3.14;

int &refVal5 = dval; // error: initializer must be an int

object

引用

54

Page 55: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

练习

练习2.15:下面哪些定义是不合法的?(a) int ival = 1.01;

(b) int &rval1 = 1.01;

(c) int &rval2 = ival;

(d) int &rval3;

练习2.17:读代码写结果int i, &ri = i;

i = 5; ri = 10;

std::cout << i << " " << ri << std::endl;

引用

55

Page 56: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

练习

练习2.16:哪些赋值是不合法的,如果合法,分别执行了什么样的操作?int i = 0, &r1 = i; double d = 0, &r2 = d;

(a) r2 = 3.14159;

(b) r2 = r1;

(c) i = r2;

(d) r1 = d;

引用

56

Page 57: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

指针

指针(pointer)是“指向”(point to)另外一种类型的复合类型指针存放所指向对象的地址

与引用类似,指针也实现了对其他对象的间接访问

与引用的不同点:指针本身就是一个对象,允许对指针赋值和拷贝,可以先后指向几个不同的对象

指针无须在定义时赋初值块作用域中定义的指针未初始化时,值不定

复合类型

57

Page 58: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

指针的定义

指针类型的声明符写成*d的形式,其中d是变量名

如果在一条语句中定义了几个指针变量,每个变量前面都必须有符号*int *ip1, *ip2; // both ip1 and ip2 are pointers to int

double dp, *dp2; // dp2 is a pointer to double; dp is a

double

指针

58

Page 59: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

获取对象的地址

指针存放某个对象的地址

要想获取对象的地址,需要使用取地址符(操作符&)

引用不是对象,没有实际地址,不能定义指向引用的指针 (例如:int &* p)

int ival = 42;

int *p = &ival; // p holds the address of ival; p is a

pointer to ival

指针

59

Page 60: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

获取对象的地址

一般地,指针的类型要和它指向的对象严格匹配double dval;

double *pd = &dval; // ok: initializer is the address of

a double

double *pd2 = pd; // ok: initializer is a pointer to

double

int *pi = pd; // error: types of pi and pd differ

pi = &dval; // error: assigning the address of a

double to a pointer to int

指针

60

Page 61: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

指针值

指针的值(即地址)应属下列4中状态之一:

指向一个对象

指向紧邻对象所占空间的下一个位置

空指针,意味着指针没有指向任何对象

无效指针,上述三种情况之外的其他值

指针

61

Page 62: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

利用指针访问对象

使用解引用符(操作符*)来访问指针所指的对象int ival = 42;

int *p = &ival; // p holds the address of ival; p is a pointer to ival

cout << *p; // * yields the object to which p points; prints 42

给解引用的结果赋值,实际上就是给指针所指的对象赋值*p = 0; // * yields the object; we assign a new value to ival through p

cout << *p; // prints 0

指针

62

Page 63: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

符号的多重含义

在声明语句中,&和*用于组成复合类型

在表达式中,又作为运算符int i = 42;

int &r = i; // & follows a type and is part of a declaration; r is a reference

int *p; // * follows a type and is part of a declaration; p is a pointer

p = &i; // & is used in an expression as the address-of operator

*p = i; // * is used in an expression as the dereference operator

int &r2 = *p; // & is part of the declaration; * is the dereference operator

指针

63

Page 64: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

空指针

空指针(null pointer)不指向任何对象int *p1 = nullptr; // equivalent to int *p1 = 0;

int *p2 = 0; // directly initializes p2 from the literal constant 0

// must #include cstdlib

int *p3 = NULL; // equivalent to int *p3 = 0;

预处理变量NULL定义在头文件cstdlib,值就是0

nullptr是一种特殊类型的字面值,可以被转换为任意其他的指针类型

把int变量直接赋给指针是错误的,即使值恰好等于0也不行int zero = 0;

pi = zero; // error: cannot assign an int to a pointer

指针

64

Page 65: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

初始化所有指针

使用未经初始化的指针是引发运行时错误的一个原因

如果指针变量未被显式初始化时,全局指针被初始化为0,局部指针未定义

建议初始化所有的指针,尽量在定义了对象后再定义指向它的指针至少初始化为nullptr或者0

不能用非0的整型字面值给指针赋值或初始化

指针

65

Page 66: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

赋值和指针

给指针赋值就是令它存放一个新的地址,从而指向一个新的对象赋值永远改变的是等号左边的对象int i = 42, ival;

int *pi = 0; // pi is initialized but addresses no object

int *pi2 = &i; // pi2 initialized to hold the address of i

int *pi3; // if pi3 is defined inside a block, pi3 is uninitialized

pi3 = pi2; // pi3 and pi2 address the same object, e.g., i

pi2 = 0; // pi2 now addresses no object

pi = &ival; // value in pi is changed; pi now points to ival

*pi = 0; // value in ival is changed; pi is unchanged

指针

66

Page 67: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

其他指针操作

在条件表达式中,如果指针的值为0,条件取false,否则取trueint ival = 1024;

int *pi = 0; // pi is a valid, null pointer

int *pi2 = &ival; // pi2 is a valid pointer that holds the address of ival

if (pi) // pi has value 0, so condition evaluates as false

// ...

if (pi2) // pi2 points to ival, so it is not 0; the condition evaluates as true

// ...

对两个类型相同的合法指针,可以使用相等(==)和不等(!=)运算符,结果为bool类型

指针

67

Page 68: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

void* 指针

void*是一种特殊的指针类型,可用于存放任意对象的地址double obj = 3.14, *pd = &obj;

// ok: void* can hold the address value of any data pointer type

void *pv = &obj; // obj can be an object of any type

pv = pd; // pv can hold a pointer to any type

void*指针可以:拿它和别的指针比较、作为函数的输入或输出,或者赋给另外一个void*指针不能直接操作void*指针所指的对象

指针

68

Page 69: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

练习

练习2.20:请叙述下面代码的作用int i = 42;

int *p1 = &i;

*p1 = *p1 * *p1;

练习2.21:下面定义中有非法的吗?int i = 0;

(a) double* dp = &i;

(b) int *ip = i;

(c) int *p = &i;

指针

69

Page 70: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

练习

练习2.24:为什么p合法而lp非法?int i = 42; void *p = &i; long *lp = &i;

练习2.32:下面代码是否合法?如果非法,请设法将其修改正确int null = 0, *p = null;

指针

70

Page 71: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

理解复合类型的声明

变量定义包括一个基本数据类型和一组声明符

一条定义语句可以定义出不同类型的变量

类型修饰符是声明符的一部分

// i is an int; p is a pointer to int; r is a reference to int

int i = 1024, *p = &i, &r = i;

71

Page 72: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

定义多个变量

涉及指针或引用的声明,一般有两种写法

把修饰符和变量标识符写在一起

int *p1, *p2; // both p1 and p2 are pointers to int

把修饰符和类型名写在一起,每条语句只定义一个变量

int* p1; // p1 is a pointer to int

int* p2; // p2 is a pointer to int

理解复合类型的声明

72

Page 73: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

指向指针的指针

指针本身是内存中的对象,也有自己的地址

**表示指向指针的指针,***表示指向指针的指针的指针int ival = 1024;

int *pi = &ival; // pi points to an int

int **ppi = &pi; // ppi points to a pointer to an int

理解复合类型的声明

73

Page 74: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

指向指针的指针

解引用int型指针得到int型对象;解引

用指向指针的指针得到一个指针,两次解引用才能访问所指向的对象

cout << "The value of ival\n"

<< "direct value: " << ival << "\n"

<< "indirect value: " << *pi << "\n"

<< "doubly indirect value: " << **ppi

<< endl;

理解复合类型的声明

74

Page 75: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

指针的引用

引用本身不是一个对象,不能定义指向引用的指针

指针是对象,可以定义指针的引用(refenrence to pointer)int i = 42;

int *p; // p is a pointer to int

int *&r = p; // r is a reference to the pointer p

r = &i; // r refers to a pointer; assigning &i to r makes p point to i

*r = 0; // dereferencing r yields i, the object to which p points; changes i to 0

理解复合类型的声明

75

Page 76: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

指针的引用

int *&r = p; // r is a reference to the pointer p

要理解r的类型,可以从右向左阅读r的定义

离变量名最近的符号(&)对变量的类型有最直接的影响,因此r是一个引用

声明符的其余部分用以确定r引用对象的类型,符号*说明r引用的是一个指针

声明的基本类型部分指出r引用的是一个int型指针

理解复合类型的声明

76

Page 77: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

练习

练习2.25:说明下列变量的类型(a) int* ip, i, &r = i;

(b) int i, *ip = 0;

(c) int* ip, ip2;

理解复合类型的声明

77

Page 78: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

const限定符

const限定的变量在运行时不能改变其值(即,运行时只读常量)

const对象创建后其值就不能改变,所以必须初始化初始值可以是任意复杂的表达式const int bufSize = 512; // input buffer size

bufSize = 512; // error: attempt to write to const object

const int i = get_size(); // ok: initialized at run time

const int j = 42; // ok: initialized at compile time

const int k; // error: k is uninitialized const

78

Page 79: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

const对象仅在文件内有效

默认情况下,const对象被设定为仅在文件内有效多个文件中出现了同名的const变量时,等同于在不同文件中分别定义了独立的变量

如果想在多个文件间共享const对象,必须在变量的定义和声明前加extern// file_1.cc defines and initializes a const that is accessible to other files

extern const int bufSize = fcn();

// file_1.h

extern const int bufSize; // same bufSize as defined in file_1.cc

const

限定符

79

Page 80: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

练习

练习2.26:下面哪些语句是合法的?(a) const int buf;

(b) int cnt = 0;

(c) const int sz = cnt;

(d) ++cnt; ++sz;

const

限定符

80

Page 81: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

const的引用

可以把引用绑定到const对象上,称之为对常量的引用(reference to const)

不能通过对常量的引用修改它所绑定的对象const int ci = 1024;

const int &r1 = ci; // ok: both reference and

underlying object are const

r1 = 42; // error: r1 is a reference to const

int &r2 = ci; // error: non const reference to a

const object

const

限定符

81

Page 82: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

初始化

引用的类型必须与其所引用对象的类型一致,但有两个例外

第一种例外情形:初始化常量引用时允许用任意表达式作为初始值,只要该表达式结果能被转换成引用的类型允许常量引用绑定非常量的对象、字面值,甚至是个一般(右值)表达式int i = 42;

const int &r1 = i; // we can bind a const int& to a plain intobject

const int &r2 = 42; // ok: r1 is a reference to const

const int &r3 = r1 * 2; // ok: r3 is a reference to const

int &r4 = r1 * 2; // error: r4 is a plain, non const reference

const

的引用

82

Page 83: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

初始化

当一个常量引用绑定到另外一种类型时,实际上绑定到一个临时对象

临时(temporary)对象是编译器暂存表达式的求值结果时临时创建的一个未命名对象

double dval = 3.14;

const int &ri = dval;

编译器把上述代码转换为:

const int temp = dval; // create a temporary const int

from the double

const int &ri = temp; // bind ri to that temporary

const

的引用

83

Page 84: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

引用非const对象

常量引用仅对引用可参与的操作做出了限定(只读),但对于引用的对象本身是不是一个常量未作限定int i = 42;

int &r1 = i; // r1 bound to i

const int &r2 = i; // r2 also bound to i; but cannot be

used to change i

r1 = 0; // r1 is not const; i is now 0

r2 = 0; // error: r2 is a reference to const

const

的引用

84

Page 85: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

指针和const

指向常量的指针(pointer to const)不能用于改变其所指对象的值(只读指针)

要想存放常量对象的地址,只能使用指向常量的指针

const double pi = 3.14; // pi is const; its value may not

be changed

double *ptr = &pi; // error: ptr is a plain pointer

const double *cptr = &pi; // ok: cptr may point to a

double that is const

*cptr = 42; // error: cannot assign to *cptr

85

Page 86: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

指针和const

指针的类型必须与其所指对象的类型一致,但有两个例外

第一种例外情形:允许一个指向常量的指针指向一个非常量对象

指向常量的指针(只读指针)仅仅要求不能通过该指针改变对象的值

double dval = 3.14; // dval is a double; its value

can be changed

const double *cptr = &dval; // ok: but can't

change dval through cptr of ‘const double *’

86

Page 87: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

const指针

常量指针(const pointer):允许把指针本身定义为常量把const放在*后用以说明指针是一个常量

常量指针必须初始化,之后其值不能再改变(无法再指向其他对象)int errNumb = 0;

int *const curErr = &errNumb; // curErr will always point to errNumb

const double pi = 3.14159;

const double *const pip = &pi; // pip is a const pointer to a const object

指针和c

onst

87

Page 88: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

const指针

能否通过常量指针修改其所指对象的值,取决于所指对象的类型指向常量的常量指针,不能修改其所指对象的值;而指向非常量的常量指针可以修改

const double pi = 3.14159;

const double *const pip = &pi;

*pip = 2.72; // error: pip is a pointer to const

// if the object to which curErr points (i.e., errNumb) is nonzero

int errNumb = 0, *const curErr = &errNumb;

if (*curErr) {

errorHandler();

*curErr = 0; // ok: reset the value of the object to which curErris bound

}

指针和c

onst

88

Page 89: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

练习

练习2.27:下面哪些初始化是合法的?(a) int i = -1, &r = 0;

(b) int *const p2 = &i2;

(c) const int i = -1, &r = 0;

(d) const int *const p3 = &i2;

(e) const int *p1 = &i2;

(f) const int &const r2;

(g) const int i2 = i, &r = i;

指针和c

onst

89

Page 90: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

练习

练习2.28:解释

如下定义,指出其中不合法的(a) int i, *const cp;

(b) int *p1, *const p2;

(c) const int ic, &r = ic;

(d) const int *const p3;

(e) const int *p;

练习2.29:对上

题定义的变量,哪些赋值是合法的?(a) i = ic;

(b) p1 = p3;

(c) p1 = &ic;

(d) p3 = &ic;

(e) p2 = p1;

(f) ic = *p3;

指针和c

onst

90

Page 91: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

顶层和底层const

顶层const(top-level const)表示对象本身是const常量,对任何数据类型都适用

底层const(low-level const)表示指向(引用)的对象是const,与指针和引用等复合类型的基本类型部分相关

指针类型可能有独立的顶层和底层const

引用类型只可能有底层const

91

Page 92: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

顶层和底层const

int i = 0;

int *const p1 = &i; // we can't change the value of p1;

const is top-level

const int ci = 42; // we cannot change ci; const is top-

level

const int *p2 = &ci; // we can change p2; const is low-

level

const int *const p3 = p2; // right-most const is top-

level, left-most is not

const int &r = ci; // const in reference types is always

low-level

92

Page 93: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

顶层和底层const

拷贝对象时,顶层const被忽略

拷贝对象时不会改变被拷贝对象的值

int i = 0;

const int ci = 42;

i = ci; // ok: copying the value of ci; top-level const in

ci is ignored

const int *p2 = &ci;

const int *const p3 = p2;

p2 = p3; // ok: pointed-to type matches; top-level const

in p3 is ignored

93

Page 94: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

顶层和底层const

拷贝对象时,底层const从不忽略

拷入和拷出的对象必须具有相同的底层const属性,或者两个对象的类型能转换

非常量可以转化为常量,反之则不行

94

Page 95: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

顶层和底层const

int i = 0;

const int ci = 42;

const int *p2 = &ci;

const int *const p3 = p2;

int *p = p3; // error: p3 has a low-level const but p doesn't

p2 = p3; // ok: p2 has the same low-level constqualification as p3

p2 = &i; // ok: we can convert int* to const int*

int &r = ci; // error: can't bind an ordinary int& to a const int object

const int &r2 = i; // ok: can bind const int& to plain int

95

Page 96: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

练习

练习2.30:下列对象是否被声明为顶层或底层constconst int v2 = 0; int v1 = v2;

int *p1 = &v1, &r1 = v1;

const int *p2 = &v2, *const p3 = &i, &r2 = v2;

练习2.31:对上题中的声明,下面哪些赋值是合法的?r1 = v2;

p1 = p2; p2 = p1;

p1 = p3; p2 = p3;

顶层和底层c

onst

96

Page 97: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

constexpr和常量表达式

常量表达式(const expression)是指编译时就能求值且值不会改变的表达式字面值是常量表达式用常量表达式初始化的const对象也是

一个对象(或表达式)是不是常量表达式由其类型和初始值共同决定const int max_files = 20; // max_files is a constant expression

const int limit = max_files + 1; // limit is a constant expression

int staff_size = 27; // staff_size is not a constant expression

const int sz = get_size(); // sz is not a constant expression

97

Page 98: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

constexpr变量

可将变量声明为constexpr以便让编译器来验证变量是否为常量表达式

声明为constexpr的变量必须用常量表达式来初始化,该变量隐含为const

不能用普通函数作为constexpr变量的初值constexpr int mf = 20; // 20 is a constant expression

constexpr int limit = mf + 1; // mf + 1 is a constant expression

constexpr int sz = size(); // ok only if size is a constexpr function

conste

xpr

和常量表达式

98

Page 99: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

字面值类型

常量表达式的值需要在编译时计算得到,声明constexpr的类型有限制

可被声明为constexpr的类型称为字面值类型(literal type)

算术类型、指针、引用为字面值类型

自定义类型不属于字面值类型

constexpr指针(引用)的初始值必须是nullptr或者0,或者指向(引用)存储于某个固定地址的对象

conste

xpr

和常量表达式

99

Page 100: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

指针和constexpr

在constexpr声明中定义一个指针,constexpr仅对指针有效,与指针所指对象的类型无关 constexpr把它定义的变量置为顶层constconst int *p = nullptr; // p is a pointer to a const int

constexpr int *q = nullptr; // q is a const pointer to int

constexpr指针可以指向常量或非常量对象int j = 0;

constexpr int i = 42; // type of i is const int

// i and j must be defined outside any function

constexpr const int *p = &i; // p is a constant pointer to the constint i

constexpr int *p1 = &j; // p1 is a constant pointer to the int j

conste

xpr

和常量表达式

100

Page 101: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

类型别名

类型别名(type alias)是一个名字,它是某种类型的同义词

传统的方法使用关键字typedeftypedef double wages; // wages is a synonym for

double

typedef wages base, *p; // base is a synonym for

double, p for double*

声明符中可以包含类型修饰符

处理类型

101

Page 102: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

类型别名

新的方法使用别名声明(alias declaration

) 来定义类型的别名:using SI = Sales_item; // SI is a synonym for Sales_item

以关键字using开始,其后跟别名和等号,

其作用是把等号左边的名字声明为等号右侧类型的别名

只要是类型名能出现的地方,就可以使用类型别名wages hourly, weekly; // same as double hourly, weekly;

SI item; // same as Sales_item item

处理类型

102

Page 103: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

指针、常量和类型别名

把指代复合类型或常量的类型别名用到声明语句中时,常会产生意想不到的结果typedef char *pstring; // pstring is a synonym for char*

const pstring cstr = 0; // cstr is a constant pointer to char

const pstring *ps; // ps is a pointer to a constant pointer

to char

把pstring看作基本类型,const pstring表示指向char的常量指针,非指向const char的指针

不能简单地直接进行变量名替换const char *cstr = 0; // wrong interpretation of const pstring

cstr

类型别名

103

Page 104: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

auto类型说明符

auto类型说明符让编译器通过初始值来推断变量的类型auto变量必须有初始值// the type of item is deduced from the type of the result of adding val1 and val2

auto item = val1 + val2; // item initialized to the result of val1 + val2

使用auto能在一条语句中声明多个变量所有变量的初始值必须有相容的基本类型auto i = 0, *p = &i; // ok: i is int and p is a pointer to int

auto sz = 0, pi = 3.14; // error: inconsistent types for szand pi

处理类型

104

Page 105: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

复合类型、const和auto

编译器推断出来的auto类型有时候和初始值的类型并不完全一样编译器以引用对象的类型作为auto的类型int i = 0, &r = i;

auto a = r; // a is an int (r is an alias for i, which has type int)

auto一般会忽略顶层const,保留底层constconst int ci = i, &cr = ci;

auto b = ci; // b is an int (top-level const in ci is dropped)

auto c = cr; // c is an int (cr is an alias for ci whose const is top-level)

auto d = &i; // d is an int* (& of an int object is int*)

auto e = &ci; // e is const int* (& of a const object is low-level const)

auto

类型说明符

105

Page 106: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

复合类型、const和auto

如果希望推断出来的auto类型是一个顶层const,需要显式说明:int i = 0;

const int ci = i;

const auto f = ci; // deduced type of ci is int; f has type const int

声明auto类型的引用时,初始值的顶层const属性仍然保留(在新类型中成为底层const)auto &g = ci; // g is a const int& that is bound to ci

auto &h = 42; // error: we can't bind a plain reference to a literal

const auto &j = 42; // ok: we can bind a const reference to a literal

auto

类型说明符

106

Page 107: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

复合类型、const和auto

&和*只从属于某个声明符,非基本类型的一部分初始值必须有相容的auto推断的类型int i = 0;

const int ci = i;

auto k = ci, &l = i; // k is int; l is int&

auto &m = ci, *p = &ci; // m is a const int&;p is a pointer to const int

// error: type deduced from i is int; type deduced from &ci is const int

auto &n = i, *p2 = &ci;

auto

类型说明符

107

Page 108: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

练习

int i = 0, &r = i;

auto a = r;

const int ci = i, &cr = ci;

auto b = ci;

auto c = cr;

auto d = &i;

auto e = &ci;

auto &g = ci;

练习2.33:定义

如左,判断下列赋值的运行结果a = 42;

b = 42;

c = 42;

d = 42;

e = 42;

g = 42;

auto

类型说明符

108

Page 109: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

练习

练习2.35:由下列定义推断出的类型是什么?const int i = 42;

auto j = i;

const auto &k = i;

auto *p = &i;

const auto j2 = i, &k2 = i;

auto

类型说明符

109

Page 110: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

decltype类型说明符

decltype(表达式)返回表达式的类型,但并不对表达式求值decltype(f()) sum = x; // sum has whatever type f returns

若decltype使用的表达式是一个变量,则返回该变量的类型,包括顶层const和引用在内引用都是作为其绑定对象的同义词出现,只有在decltype处是例外

const int ci = 0, &cj = ci;

decltype(ci) x = 0; // x has type const int

decltype(cj) y = x; // y has type const int& and is bound to x

decltype(cj) z; // error: z is a reference and must be initialized

处理类型

110

Page 111: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

decltype和引用

把decltype应用到一个不是变量的表达式时,返回表达式结果对应的类型有些表达式向decltype返回一个引用类型// decltype of an expression can be a reference type

int i = 42, *p = &i, &r = i;

decltype(r + 0) b; // ok: addition yields an int; b is an (uninitialized) int

decltype(*p) c; // error: c is int& and must be initialized

解引用操作返回引用类型(左值),所以decltype(*p)的类型是int&,而非int

declty

pe

类型说明符

111

Page 112: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

decltype和引用

decltype( (variable) )(变量名加一层或多层括号)的结果永远是引用

decltype( variable ) 只有当variable本身是一个引用时,结果才是引用// decltype of a parenthesized variable is always a

reference

int i = 42;

decltype((i)) d; // error: d is int& and must be

initialized

decltype(i) e; // ok: e is an (uninitialized) int

declty

pe

类型说明符

112

Page 113: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

练习

练习2.36:指出下面代码中每个变量的类型以及程序结束时各自的值

int a = 3, b = 4;

decltype(a) c = a;

decltype((b)) d = a;

++c;

++d;

declty

pe

类型说明符

113

Page 114: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

练习

练习2.37:赋值是会产生引用的又一类

表达式,引用的类型就是左侧运算数(左值)的类型。也就是说,如果i是int,则表达式i=x的类型是int&。请指出下面代码中每个变量的类型和值。

int a = 3, b = 4;

decltype(a) c = a;

decltype(a = b) d = a;

declty

pe

类型说明符

114

Page 115: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

自定义数据结构

数据结构是指把一组相关的数据元素组织起来的方法以及使用这些数据的策略

例如,Sales_item类把书本的ISBN号、

销售册数及销售额等数据组织在一起,并且提供了诸如isbn函数、>>、<<

、+、+=等运算在内的一系列操作

115

Page 116: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

定义Sales_data类型

把Sales_item类的数据元素组织在一起定义一个简单些的类Sales_data

struct Sales_data {

std::string bookNo;

unsigned units_sold = 0;

double revenue = 0.0;

};

类体由花括号包围形成了一个类作用域

类内部定义的名字必须唯一,但是可以与类外部定义的名字重复

自定义数据结构

116

Page 117: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

定义Sales_data类型

结束类体的右花括号后必须跟分号可以在类体后直接定义对象

但最好不要把对象定义和类定义放在一起

struct Sales_data { /* ... */ } accum, trans, *salesptr;

// equivalent, but better way to define these objectsstruct Sales_data { /* ... */ };Sales_data accum, trans, *salesptr;

自定义数据结构

117

Page 118: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

类数据成员

类的数据成员(data member)定义了类的

对象的具体内容,每个对象有自己的一份数据成员拷贝

定义数据成员的方法和定义普通变量一样

可以为数据成员提供一个类内初始值(in-

class initializer)

创建对象时,类内初识值用于初始化数据成员;没有初始值的成员将被默认初始化– 定义Sales_data对象时,units_sold和revenue都将被初始化为0,bookNo被初始化为空字符串

定义S

ale

s_data

类型

118

Page 119: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

使用Sales_data类

0-201-78345-X 3 20.00

0-201-78345-X 2 25.00

写一段程序实现打印两笔交易记录相加结果的功能#include <iostream>

#include <string>

#include “Sales_data.h”int main()

{

Sales_data data1, data2;

// 1. code to read into data1 and data2

// 2. code to check whether data1 and data2 have the same ISBN

// and if so print the sum of data1 and data2

}

自定义数据结构

120

Page 120: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

Sales_data对象读入数据

string类提供了>>、<<和==等,功能分别是读入字符串、写出字符串和比较字符串double price = 0; // price per book, used to calculate total revenue

// read the first transactions: ISBN, number of books sold, price per book

std::cin >> data1.bookNo >> data1.units_sold >> price;

// calculate total revenue from price and units_sold

data1.revenue = data1.units_sold * price;

// read the second transaction

std::cin >> data2.bookNo >> data2.units_sold >> price;

data2.revenue = data2.units_sold * price;

交易记录的是书售出的单价,而数据结构存储总销售额

使用S

ale

s_data

121

Page 121: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

输出两个Sales_data

对象的和检查两笔交易涉及的ISBN是否相同。如果相同输出它们的和,否则报错if (data1.bookNo == data2.bookNo) {

unsigned totalCnt = data1.units_sold + data2.units_sold;double totalRevenue = data1.revenue + data2.revenue;// print: ISBN, total sold, total revenue, average price per bookstd::cout << data1.bookNo << " " << totalCnt

<< " " << totalRevenue << " ";if (totalCnt != 0)

std::cout << totalRevenue/totalCnt << std::endl;else

std::cout << "(no sales)" << std::endl;return 0; // indicate success

} else { // transactions weren't for the same ISBNstd::cerr << "Data must refer to the same ISBN"

<< std::endl;return -1; // indicate failure

}

使用S

ale

s_data

122

Page 122: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

编写自己的头文件

类通常在头文件中,而且类所在头文件的名字应与类一样

例如Sales_data类定义在Sales_data.h中

头文件通常包含那些只能被定义一次的实体,如类、const和constexpr变量

自定义数据结构

123

Page 123: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

预处理器概述

确保头文件多次包含仍能安全工作的常用技术是预处理器(preprocessor)

预处理器是在编译之前执行的一段程序,可以部分地改变我们写的程序

当预处理器看到#include标记时,会用指定的头文件的内容来代替#include

编写自己的头文件

124

Page 124: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

预处理器概述

C++程序使用预处理器来定义头文件保护符(header guard)

头文件保护符依赖于预处理变量,预处理变量有两种状态已定义(define)和未定义(undefined)

#define指令把一个名字定义为预处理变量#ifdef指令当且仅当预处理变量已定义时为真

#ifndef指令当且仅当预处理变量未定义时为真

一旦检查为真,则执行#ifdef或#ifndef后的操作直到遇到#endif指令

编写自己的头文件

125

Page 125: C++ 程序设计 2章变量和类型 - USTCstaff.ustc.edu.cn/~zhuang/cpp/lectures/CP5e02.pdf12 类型转换 赋值时的类型转换规则: 把非布尔的算术值赋给一个bool对象时,0值转换为

头文件例子

#ifndef SALES_DATA_H#define SALES_DATA_H#include <string>struct Sales_data {

std::string bookNo;unsigned units_sold = 0;double revenue = 0.0;

};#endif

整个程序中预处理变量包括头文件保护符必须唯一一般把预处理变量的名字全部大写

编写自己的头文件

126