《教学课件:《C++程序设计教程》章韵》由会员分享,可在线阅读,更多相关《教学课件:《C++程序设计教程》章韵(320页珍藏版)》请在金锄头文库上搜索。
1、C+程序设计语言程序设计语言121.1 1.1 计算机程序设计语言计算机程序设计语言1.2 C+1.2 C+程序设计语言程序设计语言 第第1章章 C+概述概述1.1 程序设计的基本概念程序设计的基本概念1. 计算机与程序计算机与程序l计算机已渗入到我们日常生活的方方面面,它几乎无所不能;计算机已渗入到我们日常生活的方方面面,它几乎无所不能;l计算机所做的一切都是人所赋予的;计算机所做的一切都是人所赋予的;l计算机程序是人向计算机发出的指令的有序集计合;计算机程序是人向计算机发出的指令的有序集计合;l人机交互需要特定的计算机语言人机交互需要特定的计算机语言程序设计语言。程序设计语言。31.1 程
2、序设计的概念程序设计的概念2. 计算机程序设计语言计算机程序设计语言 计算机的工作是受程序控制的,任何一个计算机程序必须由某种程序设计计算机的工作是受程序控制的,任何一个计算机程序必须由某种程序设计语言来描述。语言来描述。 程序设计语言经历了从程序设计语言经历了从机器语言机器语言、汇编语言汇编语言到到高级语言高级语言这样一个发展过程。这样一个发展过程。C+程序设计语言属于高级语言。程序设计语言属于高级语言。4机器语言:5l直接用计算机能识别的二进制指令来书写;直接用计算机能识别的二进制指令来书写;l直接对计算机硬件产生作用;直接对计算机硬件产生作用;l不同型号计算机的不同型号计算机的“机器语言
3、机器语言”不一样,难理解,不利不一样,难理解,不利于被掌握和推广,只有少数计算机专家或者从事相关工于被掌握和推广,只有少数计算机专家或者从事相关工作的专业技术人员才能使用。作的专业技术人员才能使用。汇编语言:6l是符号化是符号化的的机器语言,用简单助记符来代替机器语言中的机器语言,用简单助记符来代替机器语言中的操作指令;操作指令; 例如,机器语言例如,机器语言中,中,二进制代码二进制代码1011011010110110代表加法运算代表加法运算,汇编语言中,汇编语言中则用助记符则用助记符ADDADD表示。表示。l较易理解,方便推广和应用;较易理解,方便推广和应用;l需要需要编译编译成机器语言才能
4、被成机器语言才能被计算机计算机执行,但编译工作可以执行,但编译工作可以用专门的工具软件完成。用专门的工具软件完成。高级语言:7l以近似于自然语言的方式描述程序指令;以近似于自然语言的方式描述程序指令; 例如,例如, if ( x0 ) y = x ; else y = -x ; l更更易理解,更方便推广和应用;易理解,更方便推广和应用;l同样需要同样需要编译编译成机器语言才能被成机器语言才能被计算机计算机执行;执行;l例如,例如,C+、C、Basic、Java等程序设计语言均属于高级程等程序设计语言均属于高级程序设计语言。序设计语言。1.2 C+程序设计语言程序设计语言1. 简单简单C+程序框
5、架结构程序框架结构#include using namespace std;int main( ) cout 变量变量1 变量变量2 对象对象n ;1.3 C+程序设计简介程序设计简介9l只能用变量接受输入的数据;只能用变量接受输入的数据;l用户可以用以下两种方式从键盘输入数据:用户可以用以下两种方式从键盘输入数据:a)所有数据全部输入后再回车确认,但各数据之间以空格分隔;所有数据全部输入后再回车确认,但各数据之间以空格分隔;b)每个数据输入后立即回车确认。每个数据输入后立即回车确认。例:如果希望将键盘输入的整数例:如果希望将键盘输入的整数10,11, 12, 13,14 分别依次分别依次存入
6、变量存入变量 a, b, c, d, 怎样键盘输入数据?怎样键盘输入数据?int a, b, c, d;cin a;cin b c d ;C+标准输出格式为:标准输出格式为:cout 数据数据1 数据数据2 数据数据n;1.3 C+程序设计简介程序设计简介10l输出对象可以是变量、表达式或立即数;输出对象可以是变量、表达式或立即数;l当输出对象是变量或表达式时,表示输出变量或表达式所代表当输出对象是变量或表达式时,表示输出变量或表达式所代表的数据;的数据;l输出的各个数据之间应适当分隔以增强输出结果的可读性输出的各个数据之间应适当分隔以增强输出结果的可读性。例:试分析下列程序段执行之后的输出结
7、果。例:试分析下列程序段执行之后的输出结果。int a=2, b=3, c=4, d=5;cout “a+b=“a+bt“c+d=“c+dendl; cout2 的值为的值为 true; 表达式表达式 (12) + (41) 的值为的值为 0+1+1=22整型整型2. 2 基本数据类型基本数据类型 l关键字:关键字:int表示表示l分为十进制、八进制、十六进制等表现形式;分为十进制、八进制、十六进制等表现形式;l缺省进制为十进制;缺省进制为十进制;l八进制数是以数字八进制数是以数字0开头的整数开头的整数: 数码数码07,逢八进一;,逢八进一;l十六进制数是以十六进制数是以0x或或0X开头的整数
8、:数码开头的整数:数码09, af, 逢十逢十六进一,数码六进一,数码 af 大小写均可。大小写均可。例:例: 28,-3,0,+55,0236, -063,0x56EF,0X43ab,-0x857 3实型实型2. 2 基本数据类型基本数据类型 l实型数据也称浮点型,实型数据也称浮点型,l关键字:关键字:float(单精度)、(单精度)、double(双精度)。(双精度)。l实型数据可以定点表示和浮点表示实型数据可以定点表示和浮点表示定点表示,例:定点表示,例:0.1237;浮点表示(指数形式),例:浮点表示(指数形式),例:2.765E3,1e-2,-0.3e1;l关于浮点:关于浮点:E可以
9、大写或小写;可以大写或小写;E前面必须有数字(前面必须有数字(1不能省略);不能省略);E后面必须是整数。后面必须是整数。 4字符型字符型2. 2 基本数据类型基本数据类型 l关键字:关键字:charl计算机内部以字符的计算机内部以字符的ASCII码值(整数)存储字符型数码值(整数)存储字符型数据(据(1个字节)个字节)l普通字符常量书写时用单引号标记普通字符常量书写时用单引号标记例:例:%,a,9。l任何字符都可以用转义字符形式表示任何字符都可以用转义字符形式表示常用转义字符表示常用转义字符表示字符形式字符形式含含义义n换换行行a响响铃铃t水平制表符(水平制表符(Tab键键)0空字符空字符反
10、斜杠反斜杠单单引号引号双引号双引号”2. 2 基本数据类型基本数据类型 任何字符均可以用其八进制或十六进制任何字符均可以用其八进制或十六进制ASCII码值表示成转义码值表示成转义形式:形式:例如,例如,160、x70都表示字符都表示字符p。字符字符p的的ASCII码值为码值为112,其八进制为,其八进制为0160,十进制为,十进制为0x70转义表示时不可用十进制,缺省为八进制,十六进制表示时略进制标转义表示时不可用十进制,缺省为八进制,十六进制表示时略进制标志的前缀志的前缀0。所以,字符。所以,字符p的转义形式不可表示为以下形式:的转义形式不可表示为以下形式: 0160 0x70 5. 字符串
11、字符串l一对单引号内只能表示一个字符,多个字符可以组成字符串,字一对单引号内只能表示一个字符,多个字符可以组成字符串,字符串是用一对双引号括起来的字符集合。如:符串是用一对双引号括起来的字符集合。如:“Hello!”,“123456”等。等。l 字符串常量均含有一个字符串结束标志字符串常量均含有一个字符串结束标志0 。l字符串常量中所含字符的个数称为字符串的长度(不含字符串结字符串常量中所含字符的个数称为字符串的长度(不含字符串结束标志)。束标志)。6. 基本类型变量基本类型变量 l变量是在程序中可以改变的量变量是在程序中可以改变的量l变量必须先定义后使用变量必须先定义后使用变量定义格式:变量
12、定义格式: 数据类型数据类型 变量名变量名1,变量名,变量名2,变量名,变量名n;例如:例如: int a,b,c; float x;定义变量的同时可以给变量赋值(两种方式):定义变量的同时可以给变量赋值(两种方式): int a=1,b,c(3); float x=3.71;可以给已定义的变量重新赋值:可以给已定义的变量重新赋值: int a=5, b; a=10; b=20; a=a*b; 可以限定变量在程序中不允许改变:可以限定变量在程序中不允许改变: const int a=5, b(6); int const c=10; const float x(0.26), y=1.2;cons
13、tconst置于类型符号前或后;置于类型符号前或后;必须赋初值!必须赋初值! C+语言中的运算符包括算术运算符、关系运算符、语言中的运算符包括算术运算符、关系运算符、逻辑运算符、赋值运算符等,将变量、常量和运算符有逻辑运算符、赋值运算符等,将变量、常量和运算符有机地结合起来就组成了表达式。机地结合起来就组成了表达式。2. 3 运算符与表达式运算符与表达式l算术运算符算术运算符: : + +、- -、* *、/ /、% %(求模)(求模)l算术运算符均为双目运算符算术运算符均为双目运算符l除法运算分整数除和实数除除法运算分整数除和实数除l求模运算功能是求两个整数相除后的余数。求模运算功能是求两个
14、整数相除后的余数。2. 3 运算符与表达式运算符与表达式1. 算术运算符算术运算符例,写出下列各语句的输出结果。例,写出下列各语句的输出结果。 cout(1+a); /输出结果输出结果:98 cout(5/3-8); /输出结果输出结果:-7 cout(5%3*5/3.0); /输出结果输出结果:3.33333例:例: int a=5, b; b=a=a-2; coutatb、=、5的值为的值为 true (5!=4)=(8=2)的值为)的值为false 注意运算符形式上与数学表达式的差异;注意运算符形式上与数学表达式的差异; 数学表达式:数学表达式:abc C+表达式:表达式:ab&bc2.
15、 3 运算符与表达式运算符与表达式3. 关系运算符关系运算符l逻辑运算符:逻辑运算符: 逻辑非(逻辑非(!)、逻辑与()、逻辑与(&)和逻辑或()和逻辑或(|)l功能:将多个关系连接起来表达更复杂的关系功能:将多个关系连接起来表达更复杂的关系2. 3 运算符与表达式运算符与表达式4. 逻辑运算符逻辑运算符l 逻辑非:取反操作逻辑非:取反操作若若a的值为的值为true,则,则!a的值为的值为false;表达式表达式a=0与表达式与表达式!a具有相同的逻辑值具有相同的逻辑值l逻辑与逻辑与: 逻辑乘操作逻辑乘操作仅当仅当a和和b的值均为真时,的值均为真时,a&b的值为真,否则值为假。的值为真,否则值
16、为假。如:如:5&A值为真,值为真, (39)&(2=1) 值为假。值为假。l逻辑或逻辑或: 逻辑加操作逻辑加操作仅当仅当a和和b的值均为假时,的值均为假时,a|b的值为假,否则值为真。的值为假,否则值为真。如:如:(39)|(2=1)值为假。值为假。2. 3 运算符与表达式运算符与表达式aba&ba|b!a00001010111001011110逻辑运算真值表逻辑运算真值表C+语言中,当能够确定逻辑表达式的值时,运算将不再继续进行。语言中,当能够确定逻辑表达式的值时,运算将不再继续进行。2. 3 运算符与表达式运算符与表达式逻辑运算的优化:逻辑运算的优化:例例: int a=4, b=7,
17、c, d;c=(a=a+1)|(b=b+2);d=(a=0)&(b=a+b); coutatbtctdendl;(1)自增和自减运算符)自增和自减运算符 l单目运算符单目运算符l自增(自增(+)和自减()和自减(-),分别表示变量自身加),分别表示变量自身加1和减和减1操作。操作。l不能对表达式进行自增或自减运算。不能对表达式进行自增或自减运算。 2. 3 运算符与表达式运算符与表达式5. 其他运算符其他运算符例:例: int a=3,b=5, c=10, d; d=+a+b+c; coutatbtctdb?a+:+bc=ab?a+:+b;2. 3 运算符与表达式运算符与表达式条件运算符的嵌套
18、:条件运算符的嵌套: int a=1,b=2, c=3, d=4, e=5, f; f=ab ? cd ? a:d e ? d:e:c; / f=ab?(cd?a+:(de?d:e):c; l是双目运算符是双目运算符l格式:用逗号将一系列表达式列举出来格式:用逗号将一系列表达式列举出来l运算规则:依次从左到右计算各表达式,交将最后一个表达式的值运算规则:依次从左到右计算各表达式,交将最后一个表达式的值作为最终结果作为最终结果2. 3 运算符与表达式运算符与表达式(5)逗号运算符)逗号运算符 例例 int a=2, b=4, c=6, d; d =(a+=1, b+=2, c+=3,a+b+c)
19、;比较比较: int a=2, b=4, c=6, d; / 与上例变量初值相同与上例变量初值相同 d = a+=1, b+=2, c+=3,a+b+c; / 没有括号没有括号!2. 3 运算符与表达式运算符与表达式在在C+表达式中,当不同类型的运算符混合运算时,优先级高的运算表达式中,当不同类型的运算符混合运算时,优先级高的运算符将优先运算。符将优先运算。C+运算符的优先级自高向低排序规律大致如下:运算符的优先级自高向低排序规律大致如下:1.括号类运算符括号类运算符2.单目运算符单目运算符3.算术运算符算术运算符4.关系运算符关系运算符5.逻辑运算符逻辑运算符6.赋值运算符赋值运算符7.三目
20、运算符(条件运算符)三目运算符(条件运算符)8.逗号运算符运算符逗号运算符运算符C+语言中部分运算符的优先级和结合性语言中部分运算符的优先级和结合性优先级优先级运算符运算符结合性结合性1: ( ) - & + -左向右左向右2! + - - + (类类型型) * & sizeof new delete右向左右向左3* / % 左向右左向右4+ -左向右左向右6、 、=左向右左向右7= = !=左向右左向右11&左向右左向右12|左向右左向右13? :右向左右向左14=、+=、 -=、 *=、 /=、 %=右向左右向左15,左向右左向右2. 3 运算符与表达式运算符与表达式当当C+表达式中出现多
21、种类型的数据混合进行运算时,首先要进行类型表达式中出现多种类型的数据混合进行运算时,首先要进行类型转换。转换。C+的类型转换有自动类型转换、强制类型转换的类型转换有自动类型转换、强制类型转换2种。种。2. 4 类型转换类型转换1自动类型转换自动类型转换l字符型数据在表达式中以其字符型数据在表达式中以其ASCII码值(整数)参加运算;码值(整数)参加运算;l为保证精度,实型数据自动转换成双精度参加运算。为保证精度,实型数据自动转换成双精度参加运算。l赋值运算过程中,将右侧操作数转换成左边操作数的类型后赋值。赋值运算过程中,将右侧操作数转换成左边操作数的类型后赋值。l不同类型的数据自动转换成相同类
22、型的数据后再运算。转换原则是:不同类型的数据自动转换成相同类型的数据后再运算。转换原则是:将精度较低的向精度较高的转换。将精度较低的向精度较高的转换。例:例: int a=1; float x=3.5; char c=49; /将将ASCII码值为码值为49的字符赋给的字符赋给c a=x; /a的值为的值为3 coutF-Bendl; /输出整数输出整数4 coutx+2endl; /输出双精度型数输出双精度型数5.5 cout(a*6+x/2-c)end; /输出双精度型数输出双精度型数-29.252. 4 类型转换类型转换 强制类型转换也称显式类型转换,是指将一个表达式强制转强制类型转换也
23、称显式类型转换,是指将一个表达式强制转换到某个指定类型。其一般格式为:换到某个指定类型。其一般格式为: (数据类型名)表达式(数据类型名)表达式或或 数据类型名(表达式)数据类型名(表达式)2强制类型转换强制类型转换2. 4 类型转换类型转换例如:例如: cout(int)3.5; /输出整数输出整数3 coutchar(97);); /输出字符输出字符a本章结束本章结束谢谢!谢谢!2. 4 类型转换类型转换第3章 流程控制语句C+程序设计语言例例3.1 编程根据输入的球的半径求球的体积编程根据输入的球的半径求球的体积知识概要:知识概要:l程序用变量代表所要处程序用变量代表所要处理的数据;理的
24、数据;l程序从主函数的第一条程序从主函数的第一条语句开始执行,执行完语句开始执行,执行完所有的语句后程序终止;所有的语句后程序终止;l简单顺序流程不足以解简单顺序流程不足以解决复杂问题。决复杂问题。 1. 简单顺序流程简单顺序流程#includeusing namespace std;int main( ) const float pi=3.14; float v, r; coutr; v=4/3.0*pir*r*r; cout“球的体积球的体积 v=”vendl; return 0; 1 ) 最基本的if语句if (表达式) 语句;if (表达式) 语句1; 语句2; 复合语句2. if 条
25、件控制条件控制例例3.2 根据用户输入的实数,求出其绝对值并输出。根据用户输入的实数,求出其绝对值并输出。#includeusing namespace std;int main( ) float x; coutx; if (x0) x=-x; cout“该实数的绝对值为:该实数的绝对值为:”xendl; return 0; 语句语句 x=-x; 不一定执行不一定执行2. if 条件控制条件控制2 ) if else 语句语句 最基本的if语句只能表达“如果那么”的含义。而 if else 语句却能表达“如果那么否则”的含义。if (表达式) 语句1;else 语句2;2. if 条件控制条件
26、控制#include using namespace std;int main( ) int a, b; coutab; if (ab) cout“a大于b”endl; else cout“a可能比b小”endl; cout“a也可能等于b”endl; return 0; 复合语句缩进例例3.3 对用户输入的两个整数比较其大小。对用户输入的两个整数比较其大小。2. if 条件控制条件控制3 ) if 嵌套嵌套 实际应用中我们可能面临更多种选择,可以将ifelse 语句扩展:if语句的每一个分支均可能是嵌套的if语句。if (表达式1) 语句1;else if (表达式2) 语句2; else
27、if (表达式3) 语句3; else 语句4;2. if 条件控制条件控制例例3.4 编程求下列函数的值:编程求下列函数的值: 2. if 条件控制条件控制 #include using namespace std;int main(void) int x, y; coutx; if (x5) y=2; else if (x0) y=1; else if (x = = 0) y=0; else if (x=-5) y=-1; else y=-2; cout“f(x)=”yendl; return 0等于判断书写格式自由注意复杂条件的表达方式注意复杂条件的表达方式:1)if语句层层递进表示语句
28、层层递进表示 2)逻辑运算符的使用逻辑运算符的使用2. if 条件控制条件控制例例3.5 输入一个年份输入一个年份year,计算这一年,计算这一年2月份的天数月份的天数days,然后输出,然后输出days。 闰年的条件是:闰年的条件是:year能被能被4整除但不能被整除但不能被100整除,或者整除,或者year能被能被400整除。整除。 #include using namespace std;int main(void) int year, days; coutyear; if ( (year%4=0&year%100!=0) | year%400=0 ) days=29; else day
29、s=28; coutyear“年2月份的天数为:”daysendl; return 0复杂条件2. if 条件控制条件控制例例3.6 将键盘输入的四个数存入一组变量后对该组变量从大到小排序将键盘输入的四个数存入一组变量后对该组变量从大到小排序 #include using namespace std;int main( ) int a,b,c,d, t; cinabcd; if(ab) t=a; a=b; b=t; if(ac) t=a; a=c; c=t; if(ad) t=a; a=d; d=t; if(bc) t=b; b=c; c=t; if(bd) t=b; b=d; d=t; if
30、(cd) t=c; c=d; d=t; couta,b,c,db) if (bc) a=a+1; else b=b+1; else if (cd) c=c+1; else d=d+1;if (ab) if (bc) a=a+1 ; else if (cd) c=c+1; else d=d+1 ;if (ab) if (bc) a=a+1 ; else if (cd) c=c+1 ; else d=d+1 ;2. if 条件控制条件控制switch语句又称开关语句,它是C+的另一种分支方法 。switch (表达式) case 常量表达式 1: 语句序列1;break; case 常量表达式 2
31、: 语句序列2;break; case 常量表达式 n: 语句序列n;break; default: 语句序列0;表达式的结果必须是整型、字符型或枚举类型常量表达式的值必须是整型、字符型或枚举类型的常量,不能是变量表达式3. switch 开关控制开关控制例3.7 设计一个程序将从键盘上输入的百分制成绩转换成对应的五分制成绩并输出,90分以上为A,8089分为B,7079分为C,6069分为D,60分以下为E。条件语句条件语句 vs 开关语句开关语句3. switch 开关控制开关控制用条件语句实现:用条件语句实现:#include using namespace std;int main(
32、) int s; char c; cout s; if (s=90)c=A; else if (s=80) c=B; else if (s=70) c=C; else if (s=60) c=D; else c=E; cout “五分制分数为五分制分数为: ”cendl; return 0;3. switch 开关控制开关控制用开关语句实现:#includeusingnamespacestd;intmain() int s; char c; cout s; switch (s/10) case 10 : case 9 : c=A;break; case 8 : c=B ;break; case
33、 7 : c=C ;break; case 6 : c=D ;break; default : c=E ; cout“五分制分数为:”cendl; return 0;lcase 语句的顺序不重要;语句的顺序不重要;lcase后的常量互不相同;后的常量互不相同;ldefault语句的位置不重要;语句的位置不重要;l可以没有可以没有default语句;语句;l注意注意break的效果;的效果;3. switch 开关控制开关控制1*2+2*3+3*4 = ?1*2+2*3+3*4+100*101= ? 引入循环结构就可以简化程序中大量的重复操作。在C+语言中,循环结构是由 while 语句,dow
34、hile 语句和 for 语句来实现的,为了更方便地控制程序流程,C+语言还提供了两个循环辅助语句 break 和 continue 。4. 循环控制循环控制1 ) while循环循环例3.8 求1*2+2*3+3*4+100*101= ?while (表达式) 语句;#includeusingnamespacestd;intmain() int n=1, s=0; while(n=100) s=s+n*(n+1);s=s+n*(n+1); n+; n+; return 0;return 0;4. 循环控制循环控制2 ) dowhile循环循环while循环表达的是“当满足条件时一直做某事”。
35、do 语句; while (表达式) ;dowhile循环表达的是“一直做某事直到不满足条件为止”。有分号!4. 循环控制循环控制例3.9 求键盘输入的50个整数中正数之积和负数之和并分别输出。#includeusingnamespacestd;intmain() int n=1, a, s1=1, s2=0; while(na;cina; if (a0) s1*=a; if (a0) s1*=a; else s2+=a; else s2+=a; n+; n+; cout“s1=“s1endl;cout“s1=“s1endl; cout“s2=“s2endl; cout“s2=“s2endl;
36、 return 0;return 0;while while 循环:循环:#includeusingnamespacestd;intmain() int n=1, a, s1=1, s2=0; do cina;cina; if (a0) s1*=a; if (a0) s1*=a; else s2+=a; else s2+=a; n+; n+; while(n=50); cout“s1=“s1endl;cout“s1=“s1endl; cout“s2=“s2endl; cout“s2=“s2endl; return 0; return 0;dowhile dowhile 循环:循环:s1,s2的
37、初值为什么不同?4. 循环控制循环控制3 ) for 循环循环实现循环应注意三个方面的问题:实现循环应注意三个方面的问题:l控制变量的初始化控制变量的初始化l循环条件循环条件l控制变量的值的更新控制变量的值的更新for语句在书写形式上正好体现了这种语句在书写形式上正好体现了这种紧密的逻辑关系。紧密的逻辑关系。for (表达式1;表达式2;表达式3) 循环体语句;4. 循环控制循环控制1*2+2*3+3*4+100*101= ?while 循环;#includeusingnamespacestd;intmain() int n=1, s=0; while(n=100) s+=n*(n+1);s+
38、=n*(n+1); n+; n+; cout“s=“sendl; return 0;return 0;for 循环;#includeusingnamespacestd;intmain() for(int n=1, s=0; n=100; n+) s+=n*(n+1); s+=n*(n+1); cout“s=“sendl; return 0;return 0;4. 循环控制循环控制关于for循环的一些说明:for (表达式1;表达式2;表达式3) 语句;l表达式表达式1只执行一次只执行一次l表达式表达式2在每次准备循环前执行在每次准备循环前执行l表达式表达式3在每次循环后执行在每次循环后执行l各
39、表达式均可省略,但分号不各表达式均可省略,但分号不可省略可省略l表达式表达式2省略表示循环条件成立省略表示循环条件成立4. 循环控制循环控制4 ) 循环嵌套循环嵌套一个循环的循环体可以是另一个循环,称为循环嵌套。例3.10 打印8行7列的星号矩形#include using namespace std;int main( ) for( int i=0; i8; i+) for( int j=0; j7;j7; j+) cout“*”; coutendl; return 0;如果改为j=i 呢 ?4. 循环控制循环控制各种循环的循环次数也可以是不确定的各种循环的循环次数也可以是不确定的例3.11
40、 不断从键盘输入若干个整数,直到输入整数0为止,求所有输入整数的平均值。#include using namespace std;int main( ) int a, n=0; float s=0; couta; n+; for( ; a!=0 ; n+) s=s+a; couta; s=s/n; cout“s=“s/nendl; return 0;a的值决定了是否继续输入4. 循环控制循环控制5 ) continue 和和 break 语句语句continue 和 break 语句用于提前开始下一轮循环或中断所有循环例3.12 将100200之间不能被整除的整数输出。#include usi
41、ng namespace std;int main( ) for( int n=100; n=200; n+) if (n%3 = = 0) continue;continue; coutnendl; return 0;4. 循环控制循环控制例3.13:打印半径为整数120的所有圆的面积,如果面积超过100则不打印。#include using namespace std;int main( ) int r; float s, pi=3.14159; for( r=1; r100) break;break; cout“r=”rt“s=”sendl; return 0;switch语句中的bre
42、ak语句功能是否类似?4. 循环控制循环控制5. 习题习题2、求求100200间的所有素数间的所有素数4 4、求求FibonacciFibonacci数列的前数列的前2020项项(F(F1 1=1=1, F F2 2=1=1,F F3 3=F=F2 2+F+F1 1, ,F Fn n=F=Fn-1n-1+F+Fn-2n-2) )1 1、求求 a+aa+aaa+a+aa+aaa+aaa+aaaa=?a=?n个a5 5、求所有的水仙花数,即各位数字的立方和等于该数本身的求所有的水仙花数,即各位数字的立方和等于该数本身的3 3位整数位整数 如如153=1153=13 3+5+53 3+ + 3 33
43、 3。3 3、用以下公式求用以下公式求 的近似值,直到最后一项绝对值小于的近似值,直到最后一项绝对值小于1010-6-6为止为止6、用二分法求方程用二分法求方程x2-3-5x=0在在3,10区间的一个解区间的一个解。要求误差不。要求误差不 大于大于10-6课后练习5. 习题习题C + +实实 用用 教教 程程第四章数组和指针第第 四四 章章 数组和指针数组和指针4.1 数组4.4 结构体4.3 字符串4.2 指针4.5 枚举类型4.6 引用类型4.1 数组数组由具有各类特殊功能的信息(程序)组成1. 计算机系统计算机系统计算机的实体,如主机、外设等一、 计算机的软硬件概念硬件软件4.1 数组数
44、组数组是由数目固定、类型相同的若干个变量组成的有序集合,该集合的名字称为数组名,每个变量称为数组的元素。通常用数组名和下标来表示数组中的某个元素,因此数组中的元素也称为下标变量。可以像基本数据类型的变量一样对数组中的元素赋值或将其应用于其它表达式中。数组按维数的不同可分为一维数组和多维数组;按数组元素的类型不同可分为整型数组、浮点型数组、字符数组等。一、一维数组的定义和使用1.一维数组的定义定义一维数组的一般格式:存储类型;数组名后的方括号称为数组下标运算符,其内部的必须为正整数,它表示该数组所能容纳元素的最大数目。数组定义中的必须由字面常量、const类型的常量或宏定义的标识符构成,不能含有
45、变量,且它的值一定要为正整数4.1在定义数组的同时,可以对数组中的元素赋初值,称为数组的初始化。通常用初始值表实现。数组的初始化有如下几种形式:(1)在初始值表中给出数组中各元素的初始值。(2)在初始值表中给出数组中部分元素的初始值,此时系统自动将其它元素置0,此时初始值表不能为空。(3)在定义数组时,不直接指定数组的大小。此时编译系统会根据初始值表中元素的个数来确定数组的大小4.12一维数组的应用编译系统为数组分配连续的内存空间。数组中的每个元素都有一个确定的序号来表示它在内存中的位置,该序号是从零开始的。通过该序号可以引用数组中每一个元素,其一般格式如下:;其中是一个值为非负整数表达式,其
46、值表示该元素的在数组中的序号,称为元素的下标,在引用数组中的元素时,要避免下标越界。4.1数组中的元素与普通的变量在使用上没有太大区别,可以对它们赋值,也可将它们参与其它运算。一维数组所占用内存空间的大小为:sizeof()=数组大小*sizeof()4.1二、多维数组的定义和使用1.二维数组的定义二维数组的定义格式:存储类型;其中,称为数组的行数,称为数组的列数。二维数组中元素的个数为:*。4.1在引用二维数组中的元素时,要指明该元素在二维数组中所处的行号和列号。表示二维数组中元素的一般格式为:;其中和分别表示该元素所在的行和列,称为行下标和列下标。二维数组可以看作一维数组的直接推广。二维数
47、组中的元素在存储器中的排列规则是:先按行进行存放,每一行中的元素再按其列下标从小到大进行排列。4.12.二维数组的初始化对二维数组的初始化有如下几种方法:(1)以行为单位,对数组中的元素进行初始化。(2)用类似于一维数组初始化的方式对二维数组进行数组进行初始化。(3)在定义二维数组时不指定数组的行数,此时必须对该数组进行初始化,系统将根据初始化的数据,自动确定数组的行数。注意,此时不能省略数组的列数。4.13.二维数组的应用例设计一个程序,定义一个4行5列的二维数组并初始化,求出数组中元素的平均值、最大元素、最小元素以及最大元素和最小元素的位置(行号和列号)。例已知三行二列的矩阵a与二行三列矩
48、阵b,求这两矩阵的积c=ab。4.14.2 指针指针一、指针变量的说明每一个存储单元都有唯一的一个编号与其对应,该编号称为相应存储单元的地址。编译系统在对一个变量进行操作时,都是通过寻找其相应的地址而实现对相应的存储单元中的内容进行操作。可以用取地址运算符“&”来获取编译器为变量分配的地址值。一般格式为:&该运算返回变量的地址。变量的地址值通常是一个无符号整数,它也称为变量的指针,简称指针。我们也可以定义一个变量,该变量用来存放指针,我们称之为指针变量,指针变量的值是某个存储单元的地址。4.2说明一个指针变量的一般格式为:存储类型*,*;其中“*”号为指针运算符。为另一个存储单元(该存储单元中
49、存放的数据称为该指针变量所指向的数据)的地址,而存储类型和分别为该指针变量所指向数据的存储类型和类型,可简称为指针变量的类型。4.2编译系统在编译指针变量时将会有两个存储空间与其相关,一个为指针变量本身所占用的空间,由于它存放的是地址,因此该空间通常与int类型的数据所占用的空间相同,为4个字节;另一个为该指针变量所指向的数据所占用的存储空间。如图下图所示(以字符型指针ptrch1为例):4.220001ptrch1:ptrch1所指向的数据:地址为20001指针变量的初始化方法有两种:(1)利用另一个同类型变量的地址对该指针变量进行初始化。(2)第二种方法是通过强制类型转换用一个整型常数直接
50、初始化指针类型的变量。这种方法必须在清楚目前存储空间那些“空闲”的前提下才能使用,否则在程序运行过程中将会出现致命错误。4.2指针变量的值始终是一个地址,它是一个无符号整型数据。指针变量所指向的数据的类型与指针的类型一致,它的值可以用如下的方法获得:*这里的运算符“*”称为取内容运算符。该运算符的运算结果为作为其操作数的指针变量所指向的数据的值。4.2二、指针变量的操作1、指针变量的赋值运算(1)将一个变量的地址直接赋给同类型的指针变量。(2)同类型的指针之间可以互相赋值,此时这两个指针指向同一个内存单元。(3)不同类型的指针变量之间可以通过类型强制转换互相赋值。但这样的赋值通常没有价值。4.
51、2可以将一个整型常数赋给指针变量,此时必须要经过强制类型转换,这种赋值方法要慎重使用。但将0(NULL)赋给指针变量具有特殊的含义,它表示该指针变量为空,即该指针变量不指向任何存储单元。必须注意,在对指针变量所指向的存储单元赋值之前,该指针变量必须有一个确定的值,否则将是十分危险的。4.22.指针变量的算术运算指针变量常用的算术运算为将指针变量加上或减去一个整型常数。对于下面的运算:=n;计算机的实际处理如下:=sizeof()*n;指针的+、-运算与上述运算类似。4.23.指针变量的关系运算通常只进行同类型的变量之间的关系运算,对于不同类型指针之间的关系运算意义不大。常用的指针变量的关系运算
52、包括:判断一个指针是否为空:=0;判断两个指针是否指向同一个存储单元:=;比较两个指针变量的大小等。4.2三、指针与数组指针和数组有许多相通的地方,比如数组名就是可以看成是一个指针,而对于指针变量,也可以使用数组的下标运算符。数组的起始地址称为数组的指针,而将指向数组元素的指针变量称为指向数组的指针变量。利用指向数组的指针变量来处理数组中的元素是一种常用的方法。4.21.一维数组与指针在声明一个一维数组之后,该数组名就可以作为一个指针使用。但数组名并不是一个真正的指针变量,因为在编译数组的声明语句时,编译系统并不会为数组名另外分配存储空间,因此对数组名不能使用赋值语句以及+、-运算符。可以将一
53、个指针变量指向同类型数组的首地址,这样就可以通过对该指针变量访问数组元素。4.2对一维数组而言,数组名和指针有许多相通的地方,归纳如下:(1)数组名a表示数组的起始地址。可以利用a+i表示的第i+1个元素的地址,即&ai;(2)将指针变量pInt指向数组a的起始地址的方法有两种,即pInt=a或pInt=&a0;(3)在将pInt指向数组a的起始地址之后,pInt+i与a+i都表示数组a的第i+1个元素的地址,即&ai;在将pInt指向数组a的起始地址之后,下列表达式都表示同一个元素:ai、*(a+i)、*(pInt+i)、pInti等等。4.22.多维数组与指针当将一个指针指向二维数组的第一
54、个元素时,就可以用它访问该数组的所有元素。对于二维数组,首先要分清行地址(行指针)和元素地址(元素指针)。设有如下的一个二维数组:inta43=1,2,3,4,5,6,7,8,9,10,11,12;该数组可以看成是一个一维数组a,该一维数组中有四个元素:a0,a1,a2,a3,每个元素又都是一个一维数组。4.2数组名a表示的是数组的起始地址,也表示该二维数组第0行的首地址,它是一个行地址,而a0表示第0行第0个元素的地址。行地址与元素地址要注意的问题是:(1)行地址加上或减去一个常数还表示行地址;元素指针加上或减去一个常数还是元素的地址。(2)行地址前加上运算符“*”表示该行第0个元素的地址,
55、这里的符号“*”不是用来获取指针所指向的内容,它只是用来区分行地址和元素地址;而在元素指针前加运算符“*”表示该元素的值。(3)行地址的值与该行第0个元素地址的值相同。4.2根据以上的说明,对于二维数组a,下面的指针都是行地址:a、a+0、a+1、a+1+2,而a0、a1、a0+1、&a01、*a、*(a+0)、*(a+1)都是元素地址。除了上述的行地址外,还有另一类行指针,即&ai,它表示第i行的行指针。这里的符号“&”并不是取地址符号,因为编译器并没有为ai分配存储空间,这里的符号“&”只是用来区分行地址和元素地址。4.2要记住两个具有代表性的行地址:a+i和&ai,两个具有代表性的元素地
56、址:*(a+i)和ai。行地址、元素地址以及元素总结如下:4.2类型表示形式说明行地址a数组的起始地址、第0行的行地址a+i,&ai第i行的行地址a+i+j,&ai+j第i+j行的行地址元素地址*a,*(a+0),a0第0行第0列元素的地址ai,*(a+i),*&ai第i行第0列元素的地址ai+j,*(a+i)+j,*&ai+j,&aij第i行第j列元素的地址元素*a,*(a+0),*a0第0行第0列元素*ai,*(a+i)第i行第0列元素*(ai+j),*(*(a+i)+j),*(*&ai+j),aij第i行第j列元素四、动态分配内存new运算符可以为所创建的指针变量动态地分配存储空间,而运
57、算符delete则用于释放动态分配的存储空间。使用new运算符的一般格式为:=new(初始值);或=new;4.2第一种形式为所指向的数据分配大小为sizeof()个字节的连续存储空间,初始值表示为所分配的存储空间指定初始值。第二种形式为所指向的数据分配指定大小的数组空间,为整型变量或常量。对于上述两种情形,如果动态分配不成功,则new运算符返回NULL(0);如果成功分配,则new运算符返回所分配的存储空间的首地址,并将该地址赋给。4.2使用delete的一般格式为:delete;或delete;第一种格式为将动态分配给的内存空间归还给系统,第二种格式为将动态分配给的数组空间归还给系统。在用
58、new运算符为某个指针变量所指向的数据动态分配存储空间之后,必须用delete运算符撤消,否则该存储空间将一直被占用,直到关闭电脑。4.2使用new和delete运算符要注意如下几点:(1)new运算符实际上是为指针变量指定一个确定的值,即用动态的方式指定一个地址给指针变量。(2)如果new运算符动态分配内存失败(内存中没有符合要求的空闲的连续内存单元),则返回NULL(0),此时指针为空指针。此时,要进行空指针判断处理。(3)与数组一样,动态分配存放数组的内存空间时,也不能在分配空间的同时进行初始化。4.2(4)可以用new运算符动态分配多维数组空间,但要注意的是,它返回的是行指针。当撤消动
59、态分配二维数组空间时,一定要指明数组的行数,否则只能撤消第0行的内存空间,(5)用运算符new动态分配内存空间的指针必须保护起来,直到将该存储空间撤消,否则不仅会带来资源浪费,而且会引起一些意想不到的错误。4.2五、其他类型指针1、void指针void指针是空类型的指针,也称为无类型指针,它不指向任何类型,仅仅是一个地址。因此void指针不能进行指针运算,也不能直接取其指针所指向的数据的值。只有将void指针与其它类型的指针相关联,才能使用它。可以将其它类型的指针赋给void指针,但当将void指针赋给其它类型的指针时,必须进行强制类型转换。4.22、const类型指针(1)指向常量的指针指向
60、常量的指针是指指针所指向的数据为常量,定义这类指针的一般格式为:const*;或const*;指向常量的指针常用作函数的参数,此时,在函数体中不能改变该指针所指向的数据的值4.2(2)常量指针 常量指针是指指针变量的值为常量,定义常量指针的一般格式为:*const;注意,在定义常量指针时一定要对其初始化。4.2(2)指向常量的常量指针定义指向常量的常量指针的一般格式为:const*const;或const*const;4.24.3 字符串字符串一、字符数组说明字符数组的一般格式为:存储类型char;数组元素都以ASCII码值的形式存放于内存空间。字符数组的初始化:(1)通过初始值表对字符数组进
61、行初始化。初始值表中的元素为字符,各个字符之间用逗号隔开。(2)用一个字符串来初始化字符数组。此时,一定要注意字符串最后的结束标志。注意,不能将一个字符串直接赋给一个字符数组名,或数组中的某个元素。字符数组的使用与其它类型的数组类似,即可以用元素的下标访问数组中的各个元素。但由于字符数组作为一个整体可以当成字符串来处理,因此在使用上就有自己的独特之处,比如可以对字符数组进行整体输入输出等等。但在进行整体输入输出时要注意:1.字符数组的整体输入并不受数组大小的限制。2.当字符数组的整体输出时,输出到0为止4.3字符数组的元素是按其ASCII码值存放在内存单元中的。字符型数组的元素的取值范围为-1
62、28127,而无符号字符型数组元素的取值范围为0255。由于这一特性,可以对字符数组中的元素进行一些类似于整数的运算。4.3二、字符指针表示字符串的指针我们又称为字符指针,在很多的情况下都是用字符指针来表示字符串。可以象字符数组那样直接用字符串来初始化字符指针。由于字符串的特殊性,字符指针与一般指针在使用上还是存在一定的差异。我们要充分了解这些差异。4.3字符指针与一般指针的差异主要表现在:(1)字符指针可以进行如下形式的赋值:pCh1=NanJing!;而一般指针能够进行下述形式的赋值:*pInt=2;(2)如果插入操作符的左操作数为字符指针,则将输出整个字符串;如果为*pCh1,则只能输出
63、一个字符。如果插入操作符的左操作数为一般指针,则输出该指针的值,为一个地址。4.3可以将一个数组名赋给一个字符指针变量,表示将该字符指针变量指向字符数组的首地址,但反过来是不可以的,即不能将一个字符指针变量赋给一个字符数组名。可以将一个指针变量的值赋给另一个指针变量,这种赋值的结果是pCh1和pCh指向同一个存储单元,当其中一个指针变量所指向的数据的值改变时,另一个变量所指向的数据也跟着改变。在对字符指针进行操作时,要注意字符串的结束标志”0”。4.34.4 结构体结构体数组中的每一个元素属于同一种数据类型,利用数组处理大量的同类型数据是很方便的。但是在实际应用中,常常需要把不同类型而关系又非
64、常密切的数据组织在一起,形成一个整体,以便于统一管理。一种称为结构体的数据类型,可以用来描述这种类型的数据集。定义结构体类型的一般格式为:struct;其中,花括号内是结构体的内容,由若干个变量构成,这些变量的类型可以不同,它们称为结构体的成员或分量,分量名与结构体名不能相同。,在结构体的最后一定要有一个分号。4.4由于定义结构类型只是定义了一种数据类型,因此,当编译器编译结构体的定义时,并不为结构体中的成员分配任何内存空间,从而在定义结构体的成员时,不能指定成员的存储类型为auto、register、extern,但可以指定结构体成员的存储类型为static,其特点和作用将在以后介绍。4.4
65、在定义一个结构体之后,就增加了一种新的数据类型。可以创建该类型的变量。可以用如下三种方法定义结构体变量:(1)先定义结构体类型,然后利用结构名定义结构体变量。一般格式为:存储类型,;或存储类型struct,4.4与基本数据类型类似,在定义结构体类型的变量时,也可对该变量进行初始化,其方法与对数组的初始化方法类似,即采用用花括号括起来的初始值表。注意,初始值表中的数据类型必须与结构体中对应元素的类型一致(兼容的除外),否则会产生编译错误。另外,初始值表中的数据的个数不能大于结构体中元素的个数。在定义结构体类型的变量时,也可以指定变量的存储类型。4.4(2)在定义结构体类型的同时定义结构体变量。(
66、3)在定义结构体时,省略结构名,而直接定义变量。对于结构体类型的变量,在编译过程中,编译系统将为这些变量分配相应的内存空间,所分配的空间大小原则上为结构体中所有成员所占空间的总和。结构体变量也存在作用域的问题,其作用域与一般变量的作用域相同。4.4对结构体变量的使用一般是通过对其成员的引用实现的。使用结构体变量的成员的一般格式为:.其中,“是结构体成员运算符,它在所有运算符中优先级最高。4.4结构体类型变量本身也可参与一些运算:1)同类型的结构体变量可以相互赋值。2)结构体类型变量不能直接进行输入输出。3)结构体类型变量可以作为函数的参数,函数的返回值也可以是结构体类型。4.4定义结构体数组的
67、方法与定义结构体类型变量的方法类同,只是在定义时增加维数的说明,也有三种方法。在说明结构体数组时,可对它进行初始化,其方法与数组类似,有两种方法:第一种方法是将数组的每一个元素的成员的值用花括号括起来,再将数组的全部元素值用一对花括号括起来;第二种方法是在花括号内依次列出各个元素的成员值。4.44.5 枚举类型枚举类型定义枚举类型通常采用如下形式:enum,;一个枚举类型中可有若干个枚举常量,构成枚举量表,各枚举常量可为任意合法的标识符,它们之间用逗号隔开。枚举常量也称为枚举元素。编译系统给每个枚举元素指定一个整型常量值。如果在枚举类型定义中没有规定元素所取的整数值,系统把所列举的枚举元素的序
68、号(从0开始)作为对应元素的值。也可以指定枚举元素的值枚举元素可以作为整型常量使用,可以直接输出。可以用枚举类型说明枚举类型变量,而且也有与结构体类型类似的三种说明方法。在说明枚举类型变量时,也可对它进行初始化,此时可将某一个枚举元素作为它的初始值。通常将某个枚举元素赋给枚举类型的变量,但也可将相应的整数赋给枚举类型的变量,不过此时一定要进行强制类型转换。反过来,也可以将枚举变量强制转换为整型值。4.5对枚举类型变量可以使用两种运算符:赋值运算符和关系运算符。可以将一个枚举类型中的枚举元素赋给相应的枚举变量,同类型的枚举变量之间也可以相互赋值。枚举类型变量的关系运算是指同类型的枚举类型变量之间
69、的比较运算,或枚举类型变量与一个枚举元素进行的比较运算。枚举变量不能直接从键盘上输入,但可以直接输出。4.54.6 引用类型引用类型 引用是另一个变量别名,或另一个变量的同义词,引用变量依附于另一个变量而定义。定义引用类型变量的一般格式为:&=;其中为引用类型的变量,而必须是已经定义过的且与引变量同类型的变量。在定义一个引用变量时,编译系统并不会为其单独分配存储空间,因此必须对它进行初始化,将它与某个已定义的同类型的变量相关联。对枚举类型变量可以使用两种运算符:赋值运算符和关系运算符。可以将一个枚举类型中的枚举元素赋给相应的枚举变量,同类型的枚举变量之间也可以相互赋值。枚举类型变量的关系运算是
70、指同类型的枚举类型变量之间的比较运算,或枚举类型变量与一个枚举元素进行的比较运算。枚举变量不能直接从键盘上输入,但可以直接输出。4.6要注意如下几个问题:(1)在定义引用类型的变量时,不能用不同类型的变量或常量对它初始化。(2)引用可以与指针变量所指向的数据相关联,也可以与数组中的元素相关联。(3)可以定义指针类型的引用。(4)可以定义对引用类型变量的引用,但不能定义引用的引用,也不能定义引用指针和引用数组。(5)但没有空引用和void类型的引用。(6)可以为引用变量动态分配内存空间。4.6C + +实实 用用 教教 程程第五章类和对象第第 5 章章 类和对象类和对象5.1 对象5.4 函数重
71、载5.3 成员函数5.2 类的定义和使用5.5 函数的嵌套与递归5.6 构造函数与析构函数5.7 作用域与存储类型5.8 友元函数5.1 对象对象从广义上讲,人们所关心的事物都是对象,它是信息系统所针对的问题域中的人、地点和事物等概念的抽象。面向对象方法学将对象定义为:对象是一个逻辑实体,它是对一组信息以及作用于这些信息的操作的描述。也就是说,对象是将其自身所固有的状态特征或属性与可以对这些状态施加的操作结合在一起所构成的独立实体。对象的特性:(1)有一个名字作为该对象在系统中的标识(2)有一组描述它的特征的状态。(3)有一组操作。根据开发过程的不同,可将对象分为三种不同类型的对象:现实生活中
72、存在的实体:是我们所模拟系统的原始构成;问题对象:对象在问题中的抽象,它是现实对象在计算机解空间中的直接映射;计算机对象:问题对象在计算机中的表示,即问题对象的计算机实现。5.1 对象的状态是由其属性决定的,对象属性的选取应该遵循的最基本的原则是:要能反映对象的基本特征,它是对象真正需要记忆的特征或数据。从对象属性的不同来源可分为四类:(1)描述性属性(2)定义性属性(3)综合可得性属性(4)偶尔可得性属性从对象属性取值的特点上看,有三种类型的属性:单值属性、多值属性和互斥属性。5.1对象与外部对象的每一个交互都称为对象的行为。对象的行为包括该对象向别的对象发送消息以及该对象接受别的对象的消息
73、。消息是对象之间相互请求或相互协作的途径,是要求某个对象执行其中某个功能操作的规格的说明。对象的行为可分为内部行为和外部行为,相应于消息可分为私有消息和公有消息。所有公有消息的结合称为该对象的协议,它说明了对象能干什么,能提供什么服务,以及如何才能得到服务。5.15.2 类的定义和使用类的定义和使用 类是具有共同特点的一组对象的抽象,它提取该组对象的共同属性和操作,从抽象层次描述这组对象。类是创建对象的模板,以操作、表示和算法的形式(更一般地讲,是全部的内部和外部行为)完整地定义了一组对象的行为,类可以被认为是一种用户自定义的数据类型。通过类可以产生同类型的其它对象(实例),对象可以看成是类的
74、实例化。类可以作为面向对象系统的具有特定功能一个模块,可以作为模块划分的一种手段和依据。从语言角度讲,类是一种自定义的数据类型,其中既包含有描述其属性的数据,又有处理这些数据的操作(函数),它是C+封装的基本手段。在程序的构成上,类形成了一个具有特定功能的模块和一种代码共享的手段。5.2一、定义类定义类的一般格式为:classprivate:私有成员数据和成员函数;public:公有成员数据和成员函数;protected:保护成员数据和成员函数;5.2其中,一对大括号内是类的说明部分,称为类体;类体后面必须以分号结束。类体中定义的数据和函数称为该类的成员,分为成员数据和成员函数两部分。用关键字
75、private限定的成员称为私有成员,私有成员只限定在该类的内部使用;用关键字public限定的成员称为公有成员,公有成员可以被类中的成员函数和类外的函数调用;用关键字protected限定的成员称为保护成员,对保护成员的访问要求与私有成员类似5.2关键字private、public、protected的作用仅仅是限定成员的访问权限,在类体中的顺序无关紧要,同一个关键字在类体中可以被反复使用。其中private为类所默认的关键字,即在类体中没有明确地指定成员的访问权限时,系统约定这些成员为私有成员。限定访问权限的关键字的限定范围为:从该关键字后的第一个成员开始,到下一个限定访问权限的关键字之间
76、的所有的成员。类中的成员顺序是也无关紧要,且成员函数可以访问定义在其后面的成员。类可以嵌套定义。5.2类的成员可分为成员数据或成员函数。类的数据成员在类体内定义,它的定义方式和变量的定义方式相同。必须注意,在定义类时,因此此时不能对类中的成员数据初始化。另外,类中的成员数据不能使用关键字extern、auto以及register等(static除外)来限定其存储类型。5.2类的成员函数与一般的函数类似,其函数体可以在类体内定义也可以在类体外定义。在类体内实现的成员函数都是内联函数,称为内联成员函数,在类体外实现的成员函数称为外联成员函数,可以在函数实现时在其函数名前加上关键字inline表示该
77、函数是内联成员函数。在类体外定义成员函数,一定要在成员函数前加上“类名:”,运算符“:”称为作用域运算符或作用域限定符。5.2类中的成员具有类作用域,即成员的有效范围为整个类体,类的成员函数可以访问类中的任何成员,且不受位置的限制,即在类中先定义的成员函数可以访问后定义的其它成员。5.2二、对象的声明与使用类是一种导出型数据类型,或称用户自定义的数据类型,在定义类之后就可以用它来说明变量,具有类类型的变量称为类的对象,或者称为类的实例。在定义类的对象时,系统将为该对象分配一定的内存空间。5.2定义类的对象的方法与定义结构类型变量的方法类似,最常用的方法是先定义类,再定义对象,一般格式为:存储类
78、型,;还有其他两种对象的方法:在定义类的同时定义对象;在定义类的同时定义对象,但省去类名。在创建对象的同时可以对它的公有成员数据用初始化列表法进行初始化。当用一个类创建多个对象时,编译系统分别为各对象中的成员数据分配存储空间,而这些对象的成员函数的代码却是共享的。5.2对象的使用主要体现在对其成员的引用上,对象只能访问公有成员,一般格式为:.或.()这里的符号“.”称为成员选择运算符。对象只能通过公有成员函数间接地访问其私有成员或保护成员。5.2在使用对象的过程中,要注意以下几点:(1)可用成员选择运算符“.”来访问对象的公有成员。(2)同类型的对象之间可以整体赋值。(3)对象可用作函数的参数
79、,此时属于值调用;函数的返回值也可以为对象。(4)一个类的对象可作为另一个类的成员,称为对象成员。5.2三、对象数组和对象指针定义一维对象数组的一般格式为:存储类型;对象数组中的每个元素可以象一般的对象使用。注意,在说明对象数组时,不能对它进行初始化。对象数组的元素通过缺省构造函数而得到初始化。5.2定义指针类型的对象的一般格式为:存储类型*,*,;可以用new运算符为对象指针动态分配内存空间,一般格式为:new();动态分配的内存空间必须用delete运算符撤消,一般格式为:delete;对象指针访问其内部的公有成员的一般格式为:-;或-();5.25.3 成员函数成员函数函数是具有特定功能
80、的模块。一般情况下,编译时编译系统为函数产生一个接口,以供程序的其它部分使用。函数可以在类中定义,称为成员函数,也可在类外定义,称为全局函数。一、函数的定义和调用一、函数的定义和调用函数是具有特定功能的模块。在编译过程中,编函数是具有特定功能的模块。在编译过程中,编译系统为函数产生一个接口,以供程序的其它部分译系统为函数产生一个接口,以供程序的其它部分使用。使用。函数包括用户自定义函数和系统库函数。函数包括用户自定义函数和系统库函数。 库函数库函数也称为标准函数,是在也称为标准函数,是在C+C+编译系统中已经编译系统中已经预先定义的函数。预先定义的函数。 在程序设计过程中,用户可根据自己的需要
81、将一在程序设计过程中,用户可根据自己的需要将一段完成功能相对独立的代码定义为一个函数,这类段完成功能相对独立的代码定义为一个函数,这类函数称为函数称为用户自定义函数用户自定义函数。 从函数的形式来看,函数可以分为无参函数、有从函数的形式来看,函数可以分为无参函数、有参函数、无返回值函数和有返回值函数等。参函数、无返回值函数和有返回值函数等。5.3对于用户自定义的函数,要先完成函数的定义,对于用户自定义的函数,要先完成函数的定义,然后才可以调用它。根据函数定义和使用时参数的然后才可以调用它。根据函数定义和使用时参数的不同,可将函数分为两类:无参函数和有参函数。不同,可将函数分为两类:无参函数和有
82、参函数。无参函数就是没有任何参数的函数,而有参函数无参函数就是没有任何参数的函数,而有参函数就是带有一个或一个以上参数的函数。就是带有一个或一个以上参数的函数。5.31、无参函数定义无参函数的一般格式为:(void)/函数体其中,为函数返回值的类型,它可以是任一标准数据类型或导出数据类型,当没有返回值时,类型必须为void。后的括号”()”称为函数调用运算符,对于无参函数,函数调用运算符内可以为空,也可以为void。5.32、有参函数定义有参函数的一般格式为:类型()其中,为该函数的参数的类型和名字,中的参数称为形式参数或形参,形参的个数是没有限制的,当超过一个参数时,参数间一定要用逗号”,”
83、分隔开,且每个参数都要有类型说明。5.3二、函数的调用函数的功能是通过在程序中对其调用来实现的。调用一个函数,就是把控制权转去执行该函数的函数体,函数体执行完之后,再将控制权转到调用函数处。无参函数的调用格式一般为:函数名()有参函数的调用格式一般为:函数名()中的参数称为实际参数或实参。5.3函数的调用过程是:先计算各实参表达式的值函数的调用过程是:先计算各实参表达式的值( (对对有参函数有参函数) ),然后将所求的值传递给相应的形参,执,然后将所求的值传递给相应的形参,执行函数体,执行完毕再返回到函数的调用处,继续行函数体,执行完毕再返回到函数的调用处,继续执行其后继语句。执行其后继语句。
84、函数调用的使用方式如下:函数调用的使用方式如下:(1) (1) 对对于于有有返返回回值值的的函函数数,可可以以用用两两种种方方式式调调用用:一一种种方方式式是是函函数数调调用用出出现现在在表表达达式式中中;另另一一种种方方式式是是用用一一个个语语句句来来实实现现调调用用,称称为为函函数数调调用用语语句句,此此时,函数返回值不起任何的作用。时,函数返回值不起任何的作用。(2) (2) 对对于于没没有有返返回回值值的的函函数数,函函数数调调用用只只能能通通过过函数调用语句实现。函数调用语句实现。 5.3关于形参和实参,有如下几点说明:(1)定义函数时指定的形参,在未出现函数调用时,它们不占用内存中
85、的存储单元。只有在函数调用时,形参才被分配内存单元,在调用结束后,形参所占的内存单元也被释放。(2)调用时是将实参的值传递给形参,此时只是一种单向的传递关系。这是我们所说的“值传递”,形参值的改变不会影响实参的。(3)实参与形参的类型应相同。当类型不一致时,则它们应该兼容。5.3三、函数参数的传递方式1、值传递值传递的特点是:在被调用函数的执行过程中,当改变形参的值时,对应的实参的值不会发生变化,即在函数体中不能改变实参的值。在调用过程中,先求出实参表达式的值,并将该值传递给相应的形参,函数对形参的值作适当的处理,将处理结果通过函数返回值(如果有的话)带给函数调用者,而不能通过实参带回给调用者
86、5.35.33535axby(a)交换前3553ybxa(b)交换后执行函数swap(int,int)swap函数值传递示意图:2、引用传递当函数的形参为引用类型时,调用该函数的方式称为引用传递。引用传递的参数既可以作为输入参数,也可以作为输出参数,即当在函数中改变某个引用类型的形参时,其对应的实参也作相应的改变。在调用参数为引用类型的函数时,引用类型的形参所对应的实参必须为变量。5.3在调用引用类型参数的函数时,对引用类型的参数的操作实际上就是对传递给它的实参的操作,而不需要将实参拷贝一个副本给形参。因为从程序的执行效率上看,引用作为参数,在运行过程中可以节省资源。通常将占用存储空间较大的类
87、型的形参设置为引用类型。另外,对于函数名重载,一般类型的参数和同类型的引用参数不能作为判断参数不同的标准。5.33、地址传递当指针或数组作为函数参数时,实参向形参传递的是地址,称为地址传递。在函数执行过程中,可以对该参数指针所指向的数据或数组中的数据进行处理。由于此时形参和实参都是指向同一个存储单元,因此当形参的数据改变时,实参的数据也作相应的改变,此时的形参可以作为输出参数使用。5.3函数调用swap(&a,&b)示意图(a,b为一般变量):5.32pInt14pInt2ab(a)交换前执行函数swap(int*,int*)pInt1pInt242ba(b)交换后42ba(c)返回到函数调用
88、处四、函数的返回值1、一般类型的返回值如果函数名前的不为void,则称该函数有返回值。函数的返回值也称为函数值。当函数有返回值时,在函数体中必须使用return语句来返回该函数的值。return语句的一般格式为:return;或return();5.3return语句除了可以返回函数的值外,它还可以改变程序执行顺序,即当在一个函数体中执行到return语句时,立即结束该函数的执行。在无返回值的函数中也可以使用return语句,它主要用于需要立即结束函数执行的地方。这时,return语句的格式为:return;在一个函数中可以有多个return语句,遇到return语句,立刻结束函数的执行。当函
89、数有返回值时,必须保证在任何情况下都能执行到return语句,否则编译出错。5.32、指针类型的返回值函数返回类型为指针类型的一般格式为:*()/函数体函数体中的return语句的参数必须为全局变量的指针、静态变量指针或形参的指针(如果形参为指针类型或引用类型的话),不能为局部变量指针。5.3返回值为指针类型的函数的另一个作用是通过改变函数的返回值实现对它所指向的变量值的改变。5.33、引用类型的返回值函数的返回值也可以是引用类型,此时该函数的返回值一定是某个变量的引用。当函数的返回值为引用类型时,其函数体中的return语句的参数必须为全局变量、静态变量或形参(如果形参为指针类型或引用类型的
90、话),不能为局部变量。5.3五、字符串处理函数在C+系统中,提供了一系列字符串处理函数,这些函数都包含在头文件string.h中,它们能够对字符串进行操作。(1)求字符串长度的函数:strlen(str)(2)字符串拷贝函数:strcpy(str1,str2)在使用该函数时,字符串str2的长度必须小于字符数组str2的容量(3)字符串连接函数:strcat(str1,str2)也要注意必须保证str1所在的字符数组要有足够剩余空间容纳str2。5.3(4)字符串比较函数:strcmp(str1,str2)函数的返回值为整数:如果两字符串相等,则函数返回值为0;如果str1大于str2,则函数
91、返回值为1;其他情况函数返回值为-1。(5)部分字符串处理函数:字符串拷贝函数:strncpy(str1,str2,count)字符串比较函数:strncmp(str1,str2,count)字符串连接函数:strncat(str1,str2,count)5.3六、成员函数及其调用类的成员函数与一般的函数类似,其函数体可以在类体内定义也可以在类体外定义。在类体内实现的成员函数都是内联函数,称为内联成员函数,在类体外实现的成员函数称为外联成员函数,可以在函数实现时在其函数名前加上关键字inline表示该函数是内联成员函数。在类体外定义成员函数,一定要在成员函数前加上“类名:”,运算符“:”称为作
92、用域运算符或作用域限定符。5.3类中的成员具有类作用域,即成员的有效范围为整个类体,类的成员函数可以访问类中的任何成员,且不受位置的限制,即在类中先定义的成员函数可以访问后定义的其它成员。类的成员函数也称为消息。私有和保护成员函数只能被类的成员函数或友元函数访问,称它们为私有消息;而公有成员函数不仅可以被内部成员访问,而且能够被外部成员访问,则称它们为公有消息5.31、成员函数的定义在类体内定义成员函数的方式与普通函数相同。在内体外定义成员函数一般格式为:inline:()/函数体5.32、成员函数的调用对于公有成员函数,既可以在类内调用,也可以在类外通过对象直接调用,而类的私有和保护成员函数
93、则只能在类内调用。在类内部进行成员函数不受先定义后使用的限制,即类的成员函数可以调用该类的其它任何成员函数和成员数据。在类外调用成员函数主要是通过该类的对象实现的,一般格式为:.()5.3对于成员函数,有以下几点说明:1.成员函数可以直接调用类中的任一成员数据或成员函数。2.在定义一个类时,应根据需要指定其成员函数的访问权限,一般情况下,一个类必须至少有一个公有成员函数。3.普通函数具有的特征,成员函数同样也具有。比如,可以定义具有缺省参数值的成员函数、成员函数可以重载等。5.33、this指针this指针是指向当前被操作对象的指针。this指针可以显式地使用,通常隐式使用。当对象A调用一个成
94、员函数时,系统将自动为该函数指定一个隐含的参数,该参数是一个指向对象A的*this指针。这样,对于一个类的不同对象,尽管成员函数共享,但成员函数中隐含的*this指针却随对象的变化而变化,它总是指向当前对象。*this指针具有如下形式的缺省形式:*constthis;5.3七、const成员函数关键字const与成员函数结合方式有两种:一是将const放在成员函数名的前面,表示该函数返回一个常量,该返回值不可改变。二是将const放在成员函数的参数表之后,这里就是const成员函数。一般格式如下:()const;其中关键字const是函数类型的组成部分,在说明和定义函数时都不能省去。const
95、成员函数的特点是不能改变类中成员数据的值,也不能调用类中非const成员函数5.3C+规定如下两种类型的函数为不同函数:voidPrintAB();voidPrintAB()const;在这种情况下的成员函数调用原则是(1)const对象只能调用const成员函数(2)普通对象优先调用非const成员函数,如果没有非const成员函数,则调用同名的const成员函数。5.3八、静态数据成员与静态成员函数1、静态数据成员静态成员数据与静态变量的定义方式一样。静态成员数据必须有确定的值,但由于在类定义中不能成员数据直接进行初始化,因此必须在类定义的外部对静态成员数据再声明一次,并进行初始化,格式为
96、::=;在同一个程序中,当一个类有多个对象时,则这些对象中的静态成员数据都共享同一个存储空间。5.3由于在类外作了声明,因此类的静态成员数据具有全局变量的某些特征,但它仍属于类作用域。对于公有静态成员,可以通过类名直接访问,格式为::在创建任何对象之前,类的静态成员数据已经存在并可以引用。静态数据成员也可以是用户自定义类型的数据5.32、静态成员函数定义静态成员函数时只要在成员函数名前用关键字static修饰即可。静态成员函数不属于特定的对象,它不含有隐含的*this指针参数,它不能象普通成员函数那样直接访问对象中的非静态的成员(成员数据和成员函数),只能访问所在类的静态成员(成员数据和成员函
97、数)、全局变量和常量、外部函数等。5.3通过类名可直接访问静态成员函数,格式为::()静态成员函数不能为const成员函数。静态成员函数可以直接调用所属类的其他静态成员,但不能直接访问非静态成员,除非借助于所属类的对象。5.35.4 函数重载函数重载函数的重载又称函数名重载,是指同一个函数名可以有多个函数实现,或者说实现不同功能的函数可以具有相同的函数名。要实现函数名重载,它们的参数必须满足下面两个条件之一:参数的个数不同,或参数的类型不同注意:仅仅是函数返回值不同并不能区分两个函数,因此不能根据函数的返回值定义函数的重载。成员函数重载的原则与普通函数重载的原则相同,即重载函数之间靠所包含的参
98、数的类型或个数之间的差异进行区分。对于不同类中的成员函数之间以及成员函数与不属于任何类的普通函数之间不存在重载关系。5.45.5 函数的嵌套与递归函数的嵌套与递归1、函数的嵌套调用函数不允许嵌套定义,即不允许在其函数体内再定义另一个函数。但是函数之间的嵌套调用是可以的,即在定义一个函数时,在函数体内又调用另一个函数。下图是三层嵌套调用示意图。main函数b函数a函数调用a函数结束调用b函数2、函数的递归调用在C+中,有两种递归调用:直接递归,即在函数A的定义中调用函数A本身。间接递归,即在函数A的定义中调用函数B,而在函数B的定义中又调用了函数A。5.5递归和回推过程(以递归实现5!为例):5
99、.54*f(3)3*f(2)5*f(4)2*f(1)15*24=1204*6=243*2=62*1=2函数递归调用方法有如下两要素:递归公式:这是递归的先决条件,它决定问题能否用递归方法解决。结束条件:确定何时结束递归,该条件是不可缺少的而且必须是可达到的,否则会出现无穷递归。5.55.6 构造函数和析构函数构造函数和析构函数在创建对象时可借助于四种方法进行初始化:(1)用初始化列表的方法,这种方法的缺点是只能对公有成员数据初始化;(2)通过赋值语句,即将一个已经初始化的对象赋给要初始化的对象;(3)在类中定义一个成员函数,该成员函数能够对对象中的成员数据进行设置;(4)通过类中的一种特殊的成
100、员函数构造函数来进行初始化,这是一种最常用的方法,它的特点是在创建对象的同时能自动对对象中的成员数据进行初始化。1、构造函数的定义与类同名的成员函数是构造函数,它是一个特殊的成员函数,没有返回值。定义构造函数的一般格式为::()注意,构造函数没有返回值,在声明和定义构造函数不能说明它的返回类型,即使void类型也不行。5.62、对象的初始化创建对象时,首先调用构造函数,因此通常利用构造函数对对象中的成员数据进行初始化。对构造函数的调用是隐性的,在创建对象时可以向构造函数传递实参,一般格式为:();其中必须与某个构造函数的参数的类型和个数一致。5.6在创建对象引用时,由于它只是另一个对象的别名,
101、因此不会调用构造函数。同样,在创建对象指针时,也不会调用构造函数,除非用new运算符或其它方法对它初始化。5.63、构造函数的重载一个类可以有多个构造函数,与普通函数一样,只要它们的参数的个数或类型不同。在创建对象时,根据的类型和个数,系统将会自动调用与该匹配的构造函数,如果不存在相匹配的构造函数,则系统报错。与普通函数不同的是,如果在创建对象时没有向构造函数传递实参,则在对象名的后面不需要加括号,如加括号,则只是进行函数的原型说明。5.64、缺省构造函数和具有缺省参数值的构造函数(1)缺省的构造函数如果在类中没有定义构造函数,为了逻辑上的完整性,编译系统会自动产生一个缺省的构造函数,其格式为
102、::()5.6对于缺省的构造函数要注意以下几点:(1)如果在类中定义了构造函数,则编译器就不自动产生缺省的构造函数。(2)要在创建对象的同时对其中的成员数据初始化,就必须定义构造函数。(3)创建对象时,一定要要调用一个构造函数,不管是自定义的构造函数,还是缺省的构造函数。5.6(2)具有缺省参数值的构造函数具有缺省值的构造函数的定义方式与普通函数完全一样,具有相同的约束条件:(a)具有缺省参数值的构造函数有两种形式:参数表为空或参数有缺省值。(b)如果构造函数只有部分参数有缺省值,具有缺省值的参数必须在参数表的右侧。(c)在同一个类中,可以有多个具有缺省参数值的构造函数。(d)创建对象时,若有
103、多个构造函数的形参与对象的实参匹配,则会产生二义性错误。5.6五、构造函数的几类特殊用法1、自动类型转换对于用户自定义类型数据,在一定的条件下,不同类型的数据之间可以相互转换:自动转换和强制转换。这些转换都是通过构造函数实现的。只有一个参数的构造函数可以实现类型的自动转换。5.6当一个类能够创建带有一个实参(假设类型为T)的对象时,可进行下列操作:(1)在创建对象时可以用赋值语句进行初始化。(2)对已创建的对象,可以将T类型的数据赋给该对象。此时会产生一个临时对象,且当赋值完毕时立即撤消该临时对象。5.62、强制类型转换对于多个参数的构造函数,则可利用它进行强制类型转换。用构造函数进行类型强制
104、转换的一般格式:()5.63、拷贝构造函数能完成拷贝功能的构造函数称为拷贝构造函数。定义拷贝构造函数的格式如下::(const&)当用一个已有的对象对正在创建的对象进行初始化时,调用该构造函数函数。C+中的拷贝构造函数有两种形式:(1)系统自动产生。(2)用户自定义。5.6系统自动产生的拷贝构造函数能够处理绝大多数问题,但有些问题却必须利用用户自定义的拷贝构造函数。5.6六、构造函数的必要性对于有些情况,则必须要有构造函数,例如,若要避免上一小节所介绍的指针悬挂问题,必须要定义拷贝构造函数等等。1、对象数组当创建一个类对象数组时,如果不通过初始化表的方法对数组元素逐个初始化,该类必须不定义构造
105、函数、或者定义不带参数的构造函数、或者定义一个参数全部指定缺省值的构造函数。5.62、const数据成员类中常量类型数据称为const数据成员。const成员数据的声明方式与一般常量变量的声明方式一样,由于const成员数据不能在定义类时直接赋值,必须且只能在类的构造函数中采用初始化成员列表的方法对该成员数据进行初始化。5.6初始化成员列表的一般格式是:(参数表0):c1(参数表1),c2(参数表2),cn(参数表n)/构造函数体冒号后面的部分称为初始化成员列表,c1、cn可为对象成员名,也可为基本数据类型的成员数据。对象成员初始化的顺序取决于这些对象成员在类中说明的顺序,即先说明先调用,与他
106、们在成员初始化列表中的顺序无关。5.63、const对象不能改变const对象中的成员数据。除此之外,const对象只能访问对象中的const成员函数,但const对象可以调用公有成员数据。创建const对象的一般格式为:const或constconst对象不能被重新赋值,因此,在创建对象时必须对该对象中的成员数据初始化。5.64、对象成员在定义一个新类时,可以把一个已定义类的对象作为该类的成员,称为对象成员。对于含有对象成员的对象,在对该它进行初始化之前,首先要对其中的对象成员进行初始化,这要通过调用对象成员的构造函数来实现。通常采用“初始化成员列表”的方法向对象成员的构造函数传递实参。5.
107、67、析构函数在撤消对象时,要自动调用析构函数。撤消对象的方式有两种:一种方式为自动撤消,对于局部自动类型的对象,当超出其作用域时,对象将自动撤消;对于全局和静态类型的对象,当程序终止时自动撤消对象;对临时对象,在该对象参与运算之后立即撤消。另一种方式是主动撤消,即当用new运算符为对象指针动态分配存储空间,用delete释放该存储空间时将撤消该对象。5.6析构函数是一种特殊的成员函数,定义格式为::()与构造函数一样,当没有定义析构函数时,系统会自动产生析构函数。析构函数常承担撤消对象后的善后处理工作。5.65.7 作用域与存储类型作用域与存储类型一、作用域 作用域描述的是标识符起作用的范围
108、,这里的标识符可以泛指变量、常量或函数原型说明等。1、块作用域用花括号括起来的程序段构成一个块(即复合语句),在块内说明的标识符只能在该块内使用,其作用域就称为块作用域。具有块作用域的标识符的有效范围是从声明处开始,到块结束处为止,该作用域的范围是具有局部性的。因此,在块内定义的变量称为局部变量。在不同的作用域内可以有同名的标识符。对于块作用域,要注意以下几点:(1)块嵌套问题。当块A包含块B时,则在块B中可以使用在块A中定义的标识符,反过来则不行。另外当在块A中定义的标识符与块B中定义的标识符同名时,则在块B中的标识符将屏蔽块A中的同名标识符,即局部优先;5.7(2)对一些特殊情况,将作不同
109、的处理。a.对if语句或switch语句的表达式中定义的标识符,其作用域在该语句内。b.在for语句的第一个表达式中声明的标识符,其作用域为包含该for循环语句的那个块。c.函数形参的作用域为整个函数体。5.72、函数原型作用域函数原型声明(没有函数体)中参数的作用域称为函数原型作用域。此时,参数的作用域开始于函数原型声明的左括号,结束于函数原型声明的右括号。由于函数原型声明中的参数名与函数定义以及函数调用均无关,因此,函数原型声明中参数的标识符可以与函数定义中参数的标识符不同,甚至在函数原型声明时可以只列出参数的类型,而没有参数名。5.73、函数作用域 函数作用域是指在函数体内定义的标识符在
110、其定义的函数内均有效。该标识符在函数的任何位置都可以使用它,不受先定义后使用的限制,也不受函数体中嵌套块的限制。在C+中,只有标号具有函数作用域。因此,同一个函数体内的标号不能相同,但不同函数中的标号可以相同,且在一个函数中不能用goto语句调用另一个函数中的标号。5.74、文件作用域具有文件作用域的标识符不隶属于任何块。这类标识符的作用域从其声明处开始,直到文件结束。具有文件作用域的变量称为全局变量。当在块作用域内的变量与全局变量同名时,局部变量优先。此时若想在该块中调用全局变量,可通过作用域运算符“:”来实现。全局变量在程序开始运行时为它分配存储空间,直到程序运行结束时才释放所占用的存储空
111、间5.75、类作用域每个类都定义了属于自己的作用域和唯一的类型。在类体内声明的成员都属于该类的作用域,类的每个成员不同于任何其他类的成员或其他标识符(例如全局变量),尽管它们可能具有相同的名字。类的所有成员都被封装在其所限定的类作用域之内,它们对外部来说都是不可见的。成员函数可以在类体外定义,但不代表其就具有全局函数的特征。5.76、命名空间命名空间是为了防止名字污染而引入的,它可以将其中定义的名字隐藏起来,不同的名字空间中可以有相同的名字而互不干扰。命名空间实际上就是一个由程序设计者命名的内存区域,程序设计者可以根据需要指定一些有名字的空间域,把一些全局实体分别放在各个命名空间中,从而与其他
112、全局实体分隔开来。5.7定义命名空间的格式为:namespace/命名空间成员其中,namespace是定义命名空间的关键字,命名空间名为任意合法的标识符,但必须与其所在作用域内的其他标识符没有名字冲突;后面的一对大括号的内部的标识符称为命名空间成员,可以包括变量、常量、函数(可以是定义或声明)、结构体、类、模板、另一个命名空间(嵌套的命名空间)等。5.7在命名空间的的外部可以访问命名空间中定义的标识符,调用规则为::标识符其中,标识符为定义在命名空间成员名。命名空间的作用是建立一些互相分隔的作用域,可以根据需要设置许多个命名空间,每个命名空间名代表一个不同的命名空间域,不同的命名空间不能同名
113、。5.7可以采用using语句声明对命名空间或成员的默认使用声明,有两种形式:(1)对命名空间成员的默认使用声明:using:;则其之后访问该命名空间成员名时可以省略命名空间前缀。(2)对命名空间的默认使用声明:usingnamespace;则其之后访问该命名空间所有成员时都可以省略命名空间前缀。using语句可以放置在程序中的任何位置。5.7为了区分C语言的传统库,C+标准库定义了全局命名空间std,将C+库中的所有的标识符限定在该命名空间。同时,为了使用继承传统库中丰富的函数库,在std命名空间中定义了一套完全与传统库定义和功能完全相同的函数库,并放在相应的头文件中,新的头文件为在传统的头
114、文件名前加c,并去掉扩展名,例如,在传统库中有头文件stdlib.h,而在std命名空间中则为cstdlib。当然,对于C+独有的头文件,文件前没有c。5.7二、存储类型变量的存储类型可以分为两类: 动态存储变量:在程序的执行过程中为其分配存储空间,当程序运行到该变量所在作用域的结束处时系统自动收回为其分配的存储空间动态存储类型变量的生存期为所在的作用域。 静态存储变量:在程序开始执行时为其分配存储空间,程序执行结束时收回该存储空间,静态存储变量的生命期为整个程序执行过程。变量有四种存储类型:auto类型、register类型、static类型和extern类型。5.71、自动类型变量用关键字
115、auto修饰的变量称为自动类型的变量。在说明局部变量时,编译系统默认其为自动类型变量。自动类型的变量只能是局部类型的变量,不可能为全局变量。自动类型变量属于动态存储类型变量。在使用自动类型的变量之前必须对其赋初值。否则,其值为一个不确定的随机数。5.72、静态类型变量静态存储类型的变量有两种:全局变量和静态类型变量。用关键字static修饰的变量称为静态类型变量。该类型变量可以为局部变量,也可以为全局变量。全局静态变量的作用域为整个文件,而局部静态变量的作用域为它所在的那个块。当一个程序仅由一个文件组成时,在说明全局变量时,static可有可无,并无区别。倘若多个文件组成一个程序,情况就不一样
116、了。5.73、寄存器类型变量用关键字register修饰的局部变量称为寄存器类型变量,这类变量也采用动态存储的分配方式。在编译过程中,编译器不为寄存器类型的变量分配内存空间,而是直接使用CPU中的寄存器,以便提高对这类变量的存取速度。注意,由于寄存器变量不能长期保存变量的值,因此,静态变量和全局变量不能定义为寄存器类型变量。5.74、外部类型变量用关键字extern修饰的变量称为外部类型变量,外部类型变量必须是全局变量。在两种情况下需要使用外部类型变量。(1)在同一个源程序文件中,当在全局变量的定义之前使用该变量时。(2)当程序由多个文件组成时,若在一个文件中要引用在另一个文件定义的全局变量,
117、在引用前必须对所引用的变量进行外部声明。如果在某文件中定义的全局变量不想被其它文件所引用,可将该变量声明为静态全局变量5.75.8 友元函数友元函数一、友元函数在类中说明友元函数的一般格式为:friend();在中,通常包含一个与该友元函数具有友元关系的类类型的参数,该参数通常为引用类型。通过友元关系,类外函数可以通过某种机制访问类中的任何成员,而不受访问权限的约束。在使用友元函数时,要注意以下几点:(1)友元函数并不是相应类的成员函数,它没有隐含的*this指针,所以它不能象成员函数那样直接访问和修改类的成员。必须为友元函数定义相应类类型的参数或在函数体中定义相应类的对象,通过该参数或对象才
118、能访问和修改相应类的成员。(2)友元函数必须在类的定义中说明。(3)友元函数不受类中访问权限的限制,可以把它放在类的私有部分、公有部分或保护部分,其作用都是一样的。(4)友元函数的作用域与成员函数不同。5.8二、友元成员函数一个类的成员函数(包括构造函数和析构函数)也可以作为另一个类的友元函数,称为友元成员函数,声明友元成员函数的格式为:friend:(&,);该语句的作用是将中的成员函数声明为的友成员函数。5.8三、友元类可以直接把类A声明为类B的友元,这时称类A为类B的友元类,声明友元类的一般格式为:在类B中作如下的声明:friendclassA;这样类A中的任意一个成员函数都可以访问类B
119、成员。友元关系不具有交换性,友元关系也不具有传递性。5.8C + +实实 用用 教教 程程第六章运算符重载第第 6 章章 运算符重载运算符重载6.1 概述6.4 类型转换6.3 友元函数重载运算符6.2 成员函数重载运算符6.5 几个特殊运算符重载6.1 概述概述若要使某个运算符适用于自定义类型,必须在相应的类中对该运算符进行重载。运算符重载的原理是:一个运算符只是一个具有特定意义的符号,只要告诉编译系统在什么情况下如何去完成特定的操作,而这种操作的本质是通过特定的函数来实现的。不能被重载的运算符有:.:?:*(指针运算符)sizeof()注意,重载运算符时,不能改变它们的优先级和结合性,也不
120、能改变操作数的个数。可以通过类的成员函数或者友元函数进行重载。但有些运算符只能用成员函数重载,比如赋值运算符、数组下标运算符”、函数调用运算符”()”、new、delete等。运算符重载的一般格式为:operator()/成员函数重载/函数体或friendoperator()/友元函数/函数体其中,为要重载的运算符。6.16.2 成员函数重载运算符成员函数重载运算符成员函数可以重载几乎所有能够重载的运算符,用成员函数重载运算符的特点是当前对象为运算符的一个操作数。一、一元运算符重载用成员函数实现一元运算符重载的一般格式为:operator()/函数体该重载运算符的操作数为该重载运算符所在的当前
121、对象。6.2二、二元运算符重载用成员函数重载二元运算符的格式为:operator()/函数体其中,只有一个,通常为运算符的右操作数。重载二目运算符的左操作数为当前对象。6.26.3 友元函数重载运算符友元函数重载运算符用友元函数重载运算符时必须在参数中指定所有操作数,且这些参数中至少有一个为对应类的类型。一、一元运算符重载由于友元函数不能使用*this指针,因此用友元函数重载单目运算符时必须带有一个参数作为该运算符的操作数。重载类X的单目运算符的一般格式为:friendoperator(X&obj)/函数体该运算符重载函数是类X的友元。obj为该重载操作赋的操作数。对于要改变操作数的运算符(比
122、如+、-等),参数必须是引用类型或指针类型。6.3二、二元运算符重载用友元函数重载类X的二目运算符的一般格式:friendoperator(,)/函数体该运算符重载函数为类X的友元函数。在两个参数中,必须至少有一个参数为X类型的实例。其中为左操作数,而为右操作数。6.36.4 类型转换类型转换 类型转换是将一种类型的值映射为另一种类型的值,类型转换分隐式类型转换和强制类型转换两种。 类型转换函数是类的一个非静态成员函数,可以看成是运算符重载的一种特殊情况。为类X定义类型转换函数的格式为:operator()/函数体这个转换函数定义了类X到之间的映射关系。定义转换函数时应注意如下几点:(1)转换
123、函数是用户定义的成员函数,但它要是非静态的。(2)由于转换函数的函数名是类型转换的目标类型,因此,不必再为它指定返回值。另外转换函数是将本类型的数值或变量转换为其他的类型,因此也不必带参数。(3)转换函数也不能定义为友元函数,但转换函数可以被派生类所继承,也可将其声明为虚函数。6.46.5 几个特殊运算符的重载几个特殊运算符的重载1、 “+”和“-”运算符重载 “+”和“-”运算符都是单目运算符,前面的重载方法为前置。下面介绍后置的重载。重载后置运算符的一般格式为:operator+(int)/函数体或friendoperator+(X&,int)/函数体其中的整型参数没有特别的意义,只是标识
124、重载的是后置运算符。2、 赋值运算符重载系统为每一个类都生成了一个缺省的赋值运算符,在相同类型的实例之间可以直接相互赋值,甚至派生类的实例可以赋给基类实例。但对某些特殊的情形,该缺省的赋值运算符会出现指针悬挂问题。6.5缺省赋值语句产生指针悬挂问题示意图。6.5s1=s2撤消s2撤消s1出错s1s2“China!”“Computer!”指针悬挂s1“China!”“Computer!”已不存在s1s2“China!”“Computer!”无法撤消此时在类中必须对赋值运算符进行重载。对赋值运算符进行重载时要注意以下三点:一、只能利用成员函数重载,不能用友元函数重载;二、重载的赋值运算符不能被继承
125、;三、不能将赋值运算符重载函数声明为虚函数。6.53、数组下标运算符重载下标运算符的一般格式为:operator()/函数体其中为一个参数,且仅有一个参数,该参数设定了下标值,通常为整型。下标运算符必须利用类的成员函数来实现,而不能使用友元函数。6.54、函数调用运算符函数名后的括号“()”称为函数调用运算符。重载函数调用运算符的格式为:operator()()/函数体其中,为任意类型的参数,可以一个参数也没有,也可有多个参数,但不能带有缺省值的参数。在使用重载的函数调用运算符时,左操作数为实例,右操作数为中参数。6.5C + +实实 用用 教教 程程第七章继承与多态性第第7章章 继承与多态性
126、继承与多态性7.1 类的继承与派生7.4 程序设计举例7.3 虚函数与多态性7.2 虚基类7.1 类的继承与派生类的继承与派生派生的基本概念255派生与继承的实例7.1 类的继承与派生256类类的继承与派生的继承与派生的基本概念的基本概念所谓继承就是从父辈处得到属性和行为特征。类的所谓继承就是从父辈处得到属性和行为特征。类的继承,是新的类从已有类那里得到已有的特征。从继承,是新的类从已有类那里得到已有的特征。从另一个角度看,从已有类产生新类的过程就是类的另一个角度看,从已有类产生新类的过程就是类的派生。派生。被继承的已有类称为基类(或父类)。被继承的已有类称为基类(或父类)。派生出的新类称为派
127、生类。派生类同样也可以作为派生出的新类称为派生类。派生类同样也可以作为基类派生新的类,这样形成类的层次结构。基类派生新的类,这样形成类的层次结构。继承的目的:实现代码重用。继承的目的:实现代码重用。派生的目的:当新的问题出现,原有程序无法解决派生的目的:当新的问题出现,原有程序无法解决(或不能完全解决)时,需要对原有程序进行改造。(或不能完全解决)时,需要对原有程序进行改造。7.1 类的继承与派生7.1.1 派生类派生类的的定义定义派生类的定义方法class:;258派生(Derive):以基类的定义为基础,以公有、私有或保护方式继承基类的全部成员(数据成员和成员函数),另外根据需要声明派生类
128、的成员。引发的问题:引发的问题:派生类中对基类成员属性的改变派生类中对基类成员属性的改变在派生类中定义构造函数及其它成员函在派生类中定义构造函数及其它成员函数时,基类构造函数和成员函数的影响数时,基类构造函数和成员函数的影响7.1.2成员的继承及访问权限2597.1.2 成员的继承及访问成员的继承及访问权限权限不同不同继承方式的影响主要体现继承方式的影响主要体现在在派生派生类成员对基类成员的访问类成员对基类成员的访问控制控制派生派生类对象对基类成员的访问类对象对基类成员的访问控制控制继承方式继承方式公有公有继承继承 public私有继承私有继承 private保护继承保护继承 protecte
129、d2607.1.2 成员的继承及访问成员的继承及访问权限权限派生类派生类不能不能继承继承的包括:的包括:构造函数构造函数析构析构函数函数用户定义的用户定义的new运算符运算符用户定义的赋值用户定义的赋值运算符运算符友员友员关系关系261类和对象访问规则类和对象访问规则 类中三种不同属性的成员:类中三种不同属性的成员:1) private :仅可在类中直接访问:仅可在类中直接访问2) public:可在类中直接访问,:可在类中直接访问, 亦可通过类亦可通过类的对象在类外访问的对象在类外访问 对象名对象名.成员名成员名 派生类中:派生类中:public属性属性在派生类中可直接访问在派生类中可直接访
130、问 其他外部:其他外部: private属性属性 3)protected:双重性7.1.2成员的继承及访问权限262公有继承公有继承(publicpublic)class class 派生类名:派生类名:public public 基类名基类名 成员定义;成员定义; 基类的基类的publicpublic和和protectedprotected成员的访问属性在派生类成员的访问属性在派生类中保持不变,但中保持不变,但基类的基类的privateprivate成员不可访问。成员不可访问。派生类中的成员函数可以直接访问基类中的派生类中的成员函数可以直接访问基类中的publicpublic和和protec
131、tedprotected成员,但不能访问基类的成员,但不能访问基类的privateprivate成员。成员。通过派生类的对象只能访问基类的通过派生类的对象只能访问基类的publicpublic成员。成员。7.1.2 成员的继承及访问权限成员的继承及访问权限263私有继承私有继承(private)class class 派生类名:派生类名:private private 基类名基类名 成员定义;成员定义; 基类的基类的publicpublic和和protectedprotected成员以成员以privateprivate身份出身份出现在派生类中,但基类的现在派生类中,但基类的privatepri
132、vate成员不可访问。成员不可访问。派生类中的成员函数可以直接访问基类中的派生类中的成员函数可以直接访问基类中的publicpublic和和protectedprotected成员,但不能访问基类的成员,但不能访问基类的privateprivate成员。成员。通过派生类的对象不能访问基类中的任何成员。通过派生类的对象不能访问基类中的任何成员。7.1.2成员的继承及访问权限264保护继承保护继承(protected)class class 派生类名:派生类名:protected protected 基类名基类名 成员定义;成员定义; 基类的基类的publicpublic和和protectedpr
133、otected成员都以成员都以protectedprotected身身份出现在派生类中,但基类的份出现在派生类中,但基类的privateprivate成员不可访成员不可访问。问。派生类中的成员函数可以直接访问基类中的派生类中的成员函数可以直接访问基类中的publicpublic和和protectedprotected成员,但不能访问基类的成员,但不能访问基类的privateprivate成员。成员。通过派生类的对象不能访问基类中的任何成员。通过派生类的对象不能访问基类中的任何成员。7.1.2成员的继承及访问权限265派生类的派生类的成员变化规则成员变化规则 公有派生(公有派生(public)
134、基类中基类中public 和和 protected 成员在派成员在派生类中保持原有的属性。生类中保持原有的属性。 私有派生私有派生 (private) 基类中基类中public 和和 protected 成员在派成员在派生类中具有生类中具有private属性。属性。保护派生保护派生 (protected) 基类中基类中public 和和 protected 成员在派成员在派生类中具有生类中具有protected 属性。属性。7.1.2成员的继承及访问权限266ClassBase私有:私有:s 保护保护: b公有公有: g可直接访问可直接访问s, b, gClass 公有派生公有派生 私有:私有
135、:s2 保护保护: b2 公有公有: g2可直接访问可直接访问b, gs2, b2, g2 Class 私有派生私有派生 私有私有: s3 保护保护: b3 公有公有: g3可直接访问可直接访问b, g, s3,b3,g3派生类对象派生类对象可直接访问可直接访问g, g2 派生类对象派生类对象可直接访问可直接访问g3Object abase可直接访问可直接访问gClass保护继承同私有继承7.1.2成员的继承及访问权限267派生类成员访问属性的派生类成员访问属性的调整调整同名成员同名成员访问访问声明声明7.1.2成员的继承及访问权限268同名成员同名成员派生派生类中的成员名和基类中的成员名类中
136、的成员名和基类中的成员名相同相同,基类中的就自动被基类中的就自动被忽略忽略成员名成员名前加上前加上基基类名和作用域标识符类名和作用域标识符“:”访问声明访问声明在子类中将一个或多个继承的成员恢复其在子类中将一个或多个继承的成员恢复其在基类中的访问在基类中的访问权限权限用法:用法: 在派生类中重新使用的在派生类中重新使用的基类名称基类名称:成员名成员名称称;7.1.2成员的继承及访问权限2697.1.3 基类成员的初始化基类成员的初始化执行顺序执行顺序 基基类构造函数派生类构造函数派生类析构函数类构造函数派生类构造函数派生类析构函数基类析构函数基类析构函数构造构造规则:基类规则:基类 对象成员对
137、象成员-派生派生类类当当基类构造函数有参数时,派生类必须有构造函数基类构造函数有参数时,派生类必须有构造函数每个派生类负责其直接基类的的构造每个派生类负责其直接基类的的构造析构函数是独立的析构函数是独立的270基类与派生类的对应关系基类与派生类的对应关系单继承:派生类只从一个基类派生。单继承:派生类只从一个基类派生。多继承:派生类从多个基类派生。多继承:派生类从多个基类派生。多重派生:由一个基类派生出多个不多重派生:由一个基类派生出多个不同的派生类。同的派生类。多层派生:派生类又作为基类,继续多层派生:派生类又作为基类,继续派生新的类。派生新的类。7.1.4多继承271多重多重继承的继承的声明
138、方式声明方式class class 派生类名:继承方式派生类名:继承方式1 1 基类名基类名1 1,继承方式继承方式2 2 基类名基类名2 2,. 成员定义;成员定义; 注意:每一个注意:每一个“继承方式继承方式”,只用于限制对紧,只用于限制对紧随其后之基类的继承。对于不指定继承方式的,随其后之基类的继承。对于不指定继承方式的,系统默认为:系统默认为:privateprivate。7.1.4多继承272多重继承的构造函数和析构函数多重继承的构造函数和析构函数其次序和单继承下的相同:基类的构造函其次序和单继承下的相同:基类的构造函数数 对象成员的构造函数对象成员的构造函数-派生类的构造派生类的构
139、造函数函数相同层次的各基类之间取决于在派生类中相同层次的各基类之间取决于在派生类中声明时从左到右的顺序声明时从左到右的顺序析构函数的执行顺序正好与上述顺序相反析构函数的执行顺序正好与上述顺序相反7.1.4多继承273赋值兼容赋值兼容规则规则在在需要基类对象的任何地方都可以使用公有需要基类对象的任何地方都可以使用公有派生类的对象来派生类的对象来替代替代,替代有三种:,替代有三种:公有派生类对象可直接赋值给基类的对象,即公有派生类对象可直接赋值给基类的对象,即把派生类对象中从基类继承来的成员,逐个赋把派生类对象中从基类继承来的成员,逐个赋值给基类对象的成员;反之,基类对象则不可值给基类对象的成员;
140、反之,基类对象则不可以赋给派生类对象。以赋给派生类对象。公有派生类对象的地址可赋给基类指针变量。公有派生类对象的地址可赋给基类指针变量。公有派生类对象可以初始化基类引用。公有派生类对象可以初始化基类引用。7.1.5基类对象与派生类对象的赋值关系2747.2 虚基类虚基类 在多继承时,基类与派生类之间,或在多继承时,基类与派生类之间,或基类之间出现同名成员时,将出现访问基类之间出现同名成员时,将出现访问时的二义性(不确定性)时的二义性(不确定性) 采用虚采用虚函数或支配(同名覆盖)原则来解决。函数或支配(同名覆盖)原则来解决。 当派生类从多个基类派生,而这些基当派生类从多个基类派生,而这些基类又
141、从同一个基类派生,则在访问此共类又从同一个基类派生,则在访问此共同基类中的成员时,将产生二义性同基类中的成员时,将产生二义性 采用虚基类来解决。采用虚基类来解决。7.2.1派生关系中的二义性问题2757.2.1 派生关系中的二义性问题派生关系中的二义性问题classApublic:inta;voidf();/.;classBpublic:inta;voidf();/.;classC:publicA,publicB/.;二义性错误:Ccobject;cobject.a=10;/出错,具有二义性cobject.f();/出错,具有二义性276二义性的解决方法二义性的解决方法解决方法一:用类名来限定
142、(确定名域)解决方法一:用类名来限定(确定名域)派生类对象派生类对象. .基类名基类名:方法名方法名()()Ccobject;cobject.A:a=10;/正确cobject.B:f();/正确7.2.1派生关系中的二义性问题277二义性的解决方法二义性的解决方法解决方法二:同名覆盖解决方法二:同名覆盖在派生类中再定义一个同名成员函数在派生类中再定义一个同名成员函数f()f(),该函数,该函数再根据需要调用再根据需要调用: :某个基类某个基类:f():f() Ccobject;cobject.a=10;/使用C:acobject.f();/调用C:f();cobject.A:a=10;/使用
143、A:acobject.B:f();/调用B:f();7.2.1派生关系中的二义性问题278虚基类的引入虚基类的引入用于在继承时有共同基类的场合用于在继承时有共同基类的场合定义定义以以virtualvirtual修饰说明基类修饰说明基类例:例:class B1:virtual public Bclass B1:virtual public B作用作用主要用来解决多继承时可能发生的对同一基类主要用来解决多继承时可能发生的对同一基类继承多次而产生的二义性问题继承多次而产生的二义性问题. .为最远的派生类提供唯一的基类成员,而不重为最远的派生类提供唯一的基类成员,而不重复产生多次拷贝复产生多次拷贝注意
144、:注意:在第一级继承时就要将共同基类设计为虚基类。在第一级继承时就要将共同基类设计为虚基类。7.2.2 虚基类的应用虚基类的应用279多态性是面向对象程序设计的重多态性是面向对象程序设计的重要概念和要概念和机制,属于机制,属于面向对象方面向对象方法的三大特性之一法的三大特性之一。指的是发出同样的消息被不同类指的是发出同样的消息被不同类型的对象接收时导致完全不同的型的对象接收时导致完全不同的行为。行为。7.3 虚函数与多态性虚函数与多态性280联编联编(binding)(binding):把函数名与其函数体的程序:把函数名与其函数体的程序代码连接在一起。更进一步,将符号与存储代码连接在一起。更进
145、一步,将符号与存储地址联系在一起。地址联系在一起。OOPOOP中,把消息与方法联系在一起。中,把消息与方法联系在一起。静态静态联编联编 编译时编译时多态性多态性 在对源程序进行编译时确定消息与哪个方法在对源程序进行编译时确定消息与哪个方法相联系相联系。如。如:函数名重载,运算符重载:函数名重载,运算符重载。动态联编动态联编 运行时多态性运行时多态性 在程序运行时确定将要消息与哪个方法相联在程序运行时确定将要消息与哪个方法相联系。如:继承,虚函数。系。如:继承,虚函数。7.3.1 静态联编与动态联编静态联编与动态联编2817.3.2 虚函数及其应用虚函数及其应用虚函数是面向对象编程实现多态的基本
146、虚函数是面向对象编程实现多态的基本手段。手段。从父类中继承的时候,虚函数和被继承从父类中继承的时候,虚函数和被继承的函数要求具有相同的签名。在运行过的函数要求具有相同的签名。在运行过程中,运行系统将根据不同的对象类型,程中,运行系统将根据不同的对象类型,自动地选择所匹配的运行方式。自动地选择所匹配的运行方式。就是前面所介绍过的动态联编。就是前面所介绍过的动态联编。2827.3.3 虚函数的定义及用法虚函数的定义及用法l定义:定义: 在某基类中声明为在某基类中声明为 virtualvirtual,并在派生类中重新,并在派生类中重新定义的函数。(注意和虚基类区分)定义的函数。(注意和虚基类区分)l
147、形式:形式: virtual type fun_name();virtual type fun_name();l特点:特点: 具有继承性。基类中定义了虚函数,派生类中无具有继承性。基类中定义了虚函数,派生类中无论是否说明,同原型函数都自动为虚函数。论是否说明,同原型函数都自动为虚函数。283本质: 不是重载定义而是覆盖定义。被覆盖的成员函数一般由作用域修饰符:指明调用调用方式: 通过基类指针或引用,执行时会根据指针指向的对象的类,决定调用哪个函数。实现机制 编译系统采用动态联编方式来实现。7.3.3虚函数的定义及用法284实现动态联编需要的条件(1)必须把动态联编的行为定义为类的虚函数。(2)
148、类之间存在子类型关系,一般表现为一个类从另一个类公有派生而来。(3)必须先使用基类指针指向子类型的对象,然后直接或者间接使用基类指针调用虚函数。7.3.3虚函数的定义及用法7.3.3虚函数的定义及用法285使用虚函数需注意的问题在类体系中访问虚函数,应使用指向基类类型的指针或对基类类型的引用。base *p; p=&b;派生类中重新定义虚函数,该函数的返回值、参数个数、类型与基类中声明完全一致;且不能出现同样原型的非虚函数。虚拟属性具有继承性。若派生类中没有重新定义虚函数,该类的对象将使用基类中虚函数代码。虚函数必须是一个类的成员函数,不可是友员、构造或静态函数。可以将析构函数定义成虚函数。2
149、867.3.4纯虚函数与抽象基类l定义:在基类中声明为虚函数,该基类定义:在基类中声明为虚函数,该基类中不定义函数体。只声明,不实现。形中不定义函数体。只声明,不实现。形式:式: virtual virtual 类型类型 函数名函数名( (参数表参数表)=0)=0l作用:对于暂时无法实现的函数,可以作用:对于暂时无法实现的函数,可以声明为纯虚函数,派生类根据实际需要声明为纯虚函数,派生类根据实际需要定义自己的函数体。在基类中只定义其定义自己的函数体。在基类中只定义其名称和参数,可以统一实现。名称和参数,可以统一实现。287抽象类抽象类抽象类包含未实现的函数,因此只能作其抽象类包含未实现的函数,
150、因此只能作其它类的基类,不能实例化。它类的基类,不能实例化。不能实例化,所以不能作为参数类型,函不能实例化,所以不能作为参数类型,函数返回类型或显式类型转换的类型,但可数返回类型或显式类型转换的类型,但可以声明抽象类的指针或引用。以声明抽象类的指针或引用。类层次结构中包含未完全实现函数的类都类层次结构中包含未完全实现函数的类都是抽象类。是抽象类。抽象类中定义的普通函数,派生类对象可抽象类中定义的普通函数,派生类对象可以调用。以调用。一般类不可继承出派生类。一般类不可继承出派生类。7.3.4纯虚函数与抽象类C + +实实 用用 教教 程程第八章编译预处理命令第第八八章章 编译预处理命令编译预处理
151、命令8.1预处理指令include:文件包含8.3条件编译8.2预处理指令define:宏定义290主要内容所谓编译预处理是指,在对源程序进行编所谓编译预处理是指,在对源程序进行编译之前,先对源程序中的编译预处理命令进译之前,先对源程序中的编译预处理命令进行处理;然后再将处理的结果,和源程序一行处理;然后再将处理的结果,和源程序一起进行编译,以得到目标代码。主要包括以起进行编译,以得到目标代码。主要包括以下几个内容:下几个内容:文件包含宏定义条件编译2918.1 8.1 引言引言特点:特点:属于编译系统功能属于编译系统功能 ( 非非C+语言语句语言语句 )编译系统在编译前对程序中的特殊的命令所
152、编译系统在编译前对程序中的特殊的命令所进行的进行的“预预 处理处理”所有此类命令均以所有此类命令均以 “ “#” 开头开头可分为三类(文件包含、宏定义、条件编译可分为三类(文件包含、宏定义、条件编译 )292文件包含是指:一个源文件可将另一个源文件的全部内容包含进来,即将另外的文件包含到本文件之中。分别编译分别编译l文件是传统的存储单位和传统的编译单位。文件是传统的存储单位和传统的编译单位。l将一个完整的程序放入一个文件里通常是不可能的。将一个完整的程序放入一个文件里通常是不可能的。l节约重新编译时间。节约重新编译时间。8.1预处理指令include:文件包含293文件包含处理命令的格式:in
153、clude“包含文件名”或include两种格式的区别仅在于:l使用双引号:系统首先到当前目录下查找被包含文件,如果没找到,再到系统指定的“包含文件目录”(由用户在配置环境时设置)去查找。l使用尖括号:直接到系统指定的“包含文件目录”去查找。一般地说,使用双引号比较保险。8.1预处理指令include:文件包含294文件包含的优点:一个大程序,通常分为多个模块,并由多个程序员分别编程。有了文件包含处理功能,就可以将多个模块共用的数据(如符号常量和数据结构)或函数,集中到一个单独的文件中。这样,凡是要使用其中数据或调用其中函数的程序员,只要使用文件包含处理功能,将所需文件包含进来即可,不必再重复
154、定义它们,从而减少重复劳动。8.1预处理指令include:文件包含295l编译预处理时,预处理程序将查找指定的被包含文件,并将其复制到#include命令出现的位置上。 l常用在文件头部的被包含文件,称为“标题文件”或“头部文件”,常以“h”(head)作为后缀,简称头文件。在头文件中,除可包含宏定义外,还可包含外部变量定义、结构类型定义等。l一条包含命令,只能指定一个被包含文件。如果要包含n个文件,则要用n条包含命令。l如果文件1包含文件2,而文件2要用到文件3的内容,则在文件1中用include命令分别包含文件2和3,且文件3应在2之前。l文件包含可以嵌套,即被包含文件中又包含另一个文件
155、。8.1预处理指令include:文件包含2968.28.2预处预处理指令理指令definedefine:宏定:宏定义义 使用宏定义的优点使用宏定义的优点可提高源程序的可维护性可提高源程序的可维护性可提高源程序的可移植性可提高源程序的可移植性 减少源程序中重复书写字符串的工作量减少源程序中重复书写字符串的工作量在在+语语言言中中,“宏宏”分分为为不不带带参参数数的的宏宏(简简称称无无参参宏宏)和和带带参参数数的的宏宏(简简称称有有参参宏宏)两种。两种。297 不带参数的宏定义不带参数的宏定义#define 标识符标识符 字符串字符串作用:用一个指定的标识符代表一个字符串。作用:用一个指定的标识
156、符代表一个字符串。 带参数的宏定义带参数的宏定义#define 宏名(参数表)宏名(参数表) 字符串字符串作用:带参数的字符串替换。作用:带参数的字符串替换。8.2预处理指令define:宏定义298无参宏应用举例无参宏应用举例 #define M (a+b+c)/2#define M (a+b+c)/2#include #include using namespace std;using namespace std;int main() int main() int a,b,c,s;int a,b,c,s; coutinput 3 number: ;coutabc;cinabc;s=M;s=
157、M;couts=sendl;couts=sendl;return 0; return 0; 宏定义宏定义 宏名一般用大写宏名一般用大写 行尾不加行尾不加“ “;” ” 有效范围有效范围 : : 定义之后到文件定义之后到文件 结束。结束。 编译时编译时 标识符替换成字符串标识符替换成字符串,即宏替换。,即宏替换。8.2.1无参宏299无参宏的副作用#defineMa+b+c#includeusingnamespacestd;intmain()inta,b,c,s;coutabc;s=M/2;couts=sy)?x:y;类型参数化实现代码重用模板作用 l float max(float x,flo
158、at y) float max(float x,float y) return(xy)?x:y;return(xy)?x:y;l double max(double x,double y) double max(double x,double y) return(xy)?x:y;return(xy)?x:y;9.1模板的基本概念312函数模板声明格式函数模板声明格式 templat 返回类型返回类型 函数名函数名 函数体;函数体;声明关键字模板形参如:templat T min( T x, T y) return (xy)?x:y; 系统预定义数据类型用户自定义数据类型模板形参T9.2 函数模
159、板及其应用函数模板及其应用313函数模板函数模板一类函数的抽象一类函数的抽象 template AT min (AT x,AT y) return(xy)?x:y;任意类型模板函数模板函数某一具体的函数某一具体的函数void main() int i=5,j=14; float f1=20.68, f2=36.54; coutthe min of i, j is: min(i,j)endl; coutthe min of f1, f2 is: min(f1,f2)endl;实例类型9.2 函数模板及其应用函数模板及其应用3149.2 函数模板及其应用函数模板及其应用函数模板代表一类函数函数模板
160、代表一类函数实例化后表示实例化后表示某一具体的函数某一具体的函数315使用说明使用说明函数模板中可以用多个类型的参数,每个函数模板中可以用多个类型的参数,每个模板形模板形 参必须加有关键字参必须加有关键字classclass。在在templatetemplate语句与函数模板之间不允许有语句与函数模板之间不允许有其他语句其他语句用不同类型实例化后所执行的语句是相同用不同类型实例化后所执行的语句是相同的的9.2函数模板及其应用3169.3 类模板及其应用类模板及其应用类模板声明格式类模板声明格式 templat class 类名类名 ;模板关键字模板参数l类模板:类模板:对一批仅仅成员数据类型不
161、同的对一批仅仅成员数据类型不同的类的抽象类的抽象,是一种声明通用类的方法,是一种声明通用类的方法317l在类定义中,若采用通用数据类型定义1.数据成员的参数2.成员函数的参数或返回值时,在前面均需加上Type。9.3 类模板及其应用类模板及其应用318template /声明一个声明一个类类模板,类型名为模板,类型名为numtypeclass Compare /类模板名为类模板名为Comparepublic : Compare(numtype a,numtype b) x=a;y=b; numtype max() return (xy)?x:y; numtype min() return (xy)?x:y;private: numtype x,y;实例化为int的方法:Comparecmp(3,5);9.3 类模板及其应用类模板及其应用319类模板类模板一类类一类类templateclass stack ;实例化实例化某一具体的类某一具体的类 类名类名 对象名;对象名;9.3 类模板及其应用类模板及其应用320类模板代表一类类类模板代表一类类实例化后表示某一具体的类实例化后表示某一具体的类类模板类模板Compare(numtype,numtype)Compare(numtype,numtype)9.3 类模板及其应用类模板及其应用