Download presentation
Presentation is loading. Please wait.
1
第三章 高级函数特性
2
目标 引用 默认参数 内联函数 函数重载
3
3.1 函数的定义与使用 一个C++程序是由若干个源程序文件构成的,而一个源程序文件是由若干个函数构成。
函数定义的语法形式 类型标识符 函数名(形式参数表) { 语句序列 }
4
函数示例 如: void display_larger( int x, int y) { if (x<y)
cout<<"The larger is : "<<y<<"\n"; else if(x>y) cout<<"The larger is : "<<x<<"\n"; else cout<<"Two values are equal. "<<"\n"; }
5
函数的定义 在C++中定义函数时注意: (1)函数的形参及类型说明要采用新的ANSI标准,即必须放在函数名后面的括号内。
(2)当形参有多个时,必须用逗号隔开。 (3)如果函数是无参函数,括号也不能省略。 (4)所有的函数都要先定义,后使用(调用)。 (5)不能省略函数值的类型,必须表明该函数的函数值的类型,即使该函数没有返回值,也要注明函数值的类型为void。
6
函数的参数和函数的返回值 所谓调用函数是指在程序中使用了该函数。
主调函数 被调函数 调用点 1.形式参数和实际参数(形参和实参) 在调用函数时,大多数情况下,主调函数和被调函数之间有数据传递关系。而函数之间的数据传递就是靠函数的参数进行的,而对无参函数的调用,没有数据传递
7
函数的参数和函数的返回值 在定义函数时,函数名后面括号内的变量名为“形式参数”(形参)。在调用函数时,函数名后面括号内的表达式为“实际参数”(实参)。 例:void main() {int a,b,c; cin>>x>>y; c=max(a,b); cout<<“max is”<<c; } int max(int x,int y) { int z; z=x>y?x:y; return(z); }
8
函数的参数和函数的返回值 关于形参和实参说明几点: (1)实参可以是变量、常量、或表达式,但必须有确定的值。而形参必须是变量。
(2) 形参变量,只有存在发生函数调用时,形参才被分配存储单元,在调用结束时,形参所占的内存单元被释放。 (3)实参与形参的类型必须一致,否则会发生“类型不匹配”的错误。 (4)实参对形参的数据传递是“值传递”,即单向传递。由实参把数据传给形参,并且存储单元与形参是不同的单元,并将实参对应的值依次传递给形参变量。调用结束后,形参单元被释放,而实参单元保留并维持原值。
9
函数的参数和函数的返回值 2. 函数的返回值: (1)函数的返回值是通过函数中的return语句获得的,return语句的格式为:
(A) 强制程序执行的流程从被调函数返回到主调函数 (B) 给主调函数带回一个确定的函数值 如:int max(int a,int b) { return(a>b?a:b); }
10
函数的参数和函数的返回值 (2) 函数值的类型:函数返回值的类型就是在定义函数时的函数值的类型。在定义函数时,函数值说明的类型和return语句中的表达式类型不一致时,则以函数类型为准。 (3)如果被调用函数中没有return语句,为了明确表示函数“不返回值”,要用viod定义无类型。 如: viod print() { printf(“c language”); } 这样系统就保证不使函数带回任何值。
11
函数原型声明 函数声明 函数名 函数返回值的类型 函数的参数个数和类型 函数声明可以不包含参数名
12
函数的定义与函数声明间的区别 (1)函数的“定义”是一个函数功能的确立,包括指定函数名,函数返回值的类型,形参及其类型,函数体等,它是一个完整的、独立的函数单位。 (2)函数的“说明”则只是对已经定义好的函数的返回值进行类型的说明,它包括函数名,函数类型和一对括号。而不包括形参和函数体。 (3)对函数进行说明的作用是告诉系统,在本程序中将要用到的函数是什么类型,以便在主调函数中按此类型对函数值作相应的处理。
13
3.2 函数调用 1.函数调用的格式 函数名(实参数) 如果调用的是无参函数,则实参表可略去,但函数的括号不能省.
3.2 函数调用 1.函数调用的格式 函数名(实参数) 如果调用的是无参函数,则实参表可略去,但函数的括号不能省. 如果实参表中有多个参数,之间用逗号隔开,实参的类型、个数应与形参顺序一一对应。 函数通过下列三种方式完成函数调用: (1)函数调用语句:即以一个函数的调用后面加上“;”作为一个语句。如:printf(); (2)函数表达式:即函数出现在一个表达式中,这时要求函数带回一个确定的值以参加表达式的运算。如:c=2*max(a,b);
14
函数调用 (3)函数参数:以函数的调用作为一个函数的实参。 如:M=max(a,max(b,c)); 2.调用函数时的前提条件
在一个函数中调用另一个函数,需要具备的条件: (1)首先被调用的函数必须已经存在的函数。如果调用库函数,一般还应在本文件的开头用#include命令将调用有关库函数时所需用到的信息包含到本文件来。 (2)如果调用用户自己定义的函数,则必须对被调函数的原型进行说明,函数的原型包括: 函数值的类型标识符 被调用函数名(形参及其类型表); (3)对函数原型的说明,通常放在程序的顶部,也可以存放到一个头文件中,然后利用#include 语句嵌入到程序中。
15
函数调用的执行过程 main() 调fun() 结束 ① ② ④ ⑥ ⑦ ③ ⑤ 保存: 返回地址 fun() 当前现场 返回 恢复:
主调程序现场 ⑤
16
函数的嵌套调用 main{} 调fun1() 结束 fun1() 调fun2() 返回 fun2() ① ② ③ ⑦ ④ ⑤ ⑥ ⑧ ⑨
C++语言中函数的定义是平行的、独立的,所以,函 数的定义是不能嵌套进行的,但函数的调用可以。嵌套调用即在调用一个函数的过程中,又调用另一函数。 main{} 调fun1() 结束 fun1() 调fun2() 返回 fun2() ① ② ③ ⑦ ④ ⑤ ⑥ ⑧ ⑨
17
函数的嵌套调用 在本例中,main函数在执行过程中,调用了函数a,而函数a中又调用了函数b,所以,这就是一种嵌套调用。
要注意:在函数嵌套调调用过程中程序执行的流程和返回点的问题。
18
函数的递归调用 1.函数递归调用的概念 函数的递归调用就是当一个函数在执行的过程中,出现了直接或间接地调用函数本身的函数调用方式。
下面定义求n!的函数。 long fact(long n) { if (n==1) return 1; return fact(n-1)*n; //出现了函数fact自己直接调用本 } 身的函数调用 要正确地理解函数的递归调用时,程序执行的流程和返回点。
19
函数的递归调用 递归过程的两个阶段: 递推: 未知 已知 回归:
4!=4×3! → 3!=3×2! → 2!=2×1! → 1!=1×0! → 0!=1 未知 已知 回归: 4!=4×3!=24←3!=3×2!=6←2!=2×1!=2←1!=1×0!=1←0!=1 未知 已知
20
函数的递归调用 2.函数递归调用的条件 递归调用的条件也是我们在定义一个递归函数时应该遵循的原则。
2.函数递归调用的条件 递归调用的条件也是我们在定义一个递归函数时应该遵循的原则。 (1)必须有完成函数任务的语句。如:上例求n!中的return 1; (2)有一个确定是否能避免递归调用的测试条件。如果条件不满足时就递归调用,否则就不再递归调用。 (3)有一个递归调用语句,并且该递归调用语句的参数应该逐渐逼近不满足条件,以致最后断绝递归。 (4)先测试,后递归调用。在递归函数定义中,必须先测试,后递归调用。也就是说,递归是有条件的,满足了条件后,才可以递归。
21
函数的参数传递机制 ——传递参数值 在函数被调用时才分配形参的存储单元。 实参可以是常量、变量或表达式。 实参类型必须与形参相符。
函数的参数传递机制 ——传递参数值 在函数被调用时才分配形参的存储单元。 实参可以是常量、变量或表达式。 实参类型必须与形参相符。 传递时是传递参数值,即单向传递。
22
函数的参数传递——按值传递 函数调用中复制参数的值 函数只能访问自己创建的副本 对副本进行的更改不会影响原始变量 25 100#
23
例3-11 输入两 整数交换后输出 例3-11 从键盘输入两个整数,交换次序后输出。 #include<iostream.h>
1、值调用 例3-11 从键盘输入两个整数,交换次序后输出。 #include<iostream.h> void Swap(int a,int b); int main() { int x(5),y(10); cout<<“x=”<<x<<“ y=”<<endl; Swap(x,y); cout<<“x=”<< x<<“ y=”<<endl; return 0; } void Swap(int a,int b) { int t; t = a; a = b; b = t; } 运行结果: x= y=10 x= y=10
24
a=b; 5 x 10 y a b t=a; t b=t; 在Swap子函数中 返回主函数以后 执行主函数中的函数调用 Swap(x,y);
24
25
按引用传递 函数调用中传递参数的引用 主要优点 函数可以访问主调程序中的实际变量 提供一种将多个值从被调函数返回到主调程序的机制 100#
25 100# 100
26
按引用传递——引用的概念 引用是一种特殊类型的变量,可以被认为是另一个变量的别名。 int i, j; int &ri=i; j=10;
ri=j; 注意问题: 声明一个引用时,必须同时对它进行初始化。 一旦一个引用被初始化为一个变量的引用时,就始终只能作为这一个变量的别名,不能另做他用。 引用调用 用引用作为形参的函数调用
27
向函数传递引用 2-1 引用提供对象的别名或可选名 “&”告诉编译器将变量当作引用 void swap(int&a, int& b) {
int t = a; a = b; b = t; }
28
按引用传递——示例 例3-12 void Swap(int&a,int&b) { void Swap(int &a,int &b);
#include<iostream.h> void Swap(int &a,int &b); int main() { int x(5),y(10); cout<<“x=”<<x<<“ y=”<<endl; Swap(x,y); return 0; } void Swap(int&a,int&b) { int t; t = a; a = b; b = t; } 运行结果: x= y=10 x= y=5
29
t=a; x 5 t x 的别名 a y 10 y 的别名 b a=b b=t; y 的地址
30
向函数传递引用 2-2 引用就是对象本身 不要认为 引用是指向对象的指针 引用是该对象的副本 大的数据结构按引用传递,效率非常高
31
返回引用 返回引用不是返回变量的副本 函数头中包含一个“&” int &fn(int &num) { return(num); }
void main() int n1, n2; n1 = fn(n2);
32
常量引用 用于不希望修改对象,以及要把大对象当作输入参数的情况 高效性和安全性 将引用声明为常量,不能再绑定别的对象
double distance(const point& p1, const point& p2); 将引用声明为常量,不能再绑定别的对象 int const &ri = num1;
33
3.3 函数的默认参数 调用函数时可以不指定全部参数 为可以不指定的参数提供默认值
3.3 函数的默认参数 调用函数时可以不指定全部参数 为可以不指定的参数提供默认值 函数在声明时可以预先给出默认的形参值,调用时如给出实参,则采用实参值,否则采用预先给出的默认形参值。 int add(int x = 5,int y = 6,int z = 3); main( ) { add( ); //所有这三个参数都采用默认值 add( 1,5); //第三个参数采用默认值 add(1,2,3); //不使用默认值 }
34
参数的默认值 2-1 一旦给一个参数赋了默认值,后续所有参数也都必须有默认值,即默认参数应从右至左逐渐定义。因为调用时实参取代形参是从左向右的顺序。 int add(int x,int y=6,int z=3); //正确 int add(int x=1,int y=6,int z); 调用上面声明的函数 add() , add(1,3); //错误! int add(int x=1,int y, int z=5); 调用上面声明的函数 add() , add(2); //错误! 注:如果遗漏了中间的参数,编译器将报错
35
注意 默认值的类型必须正确 默认值可以在原型或者函数定义中给出,但不能在两个位置同时给出 建议在原型声明中指定默认值
36
默认形参值与函数的调用位置 调用出现在函数体实现之前时,默认形参值必须在函数原形中给出;而当调用出现在函数体实现之后时,默认形参值需在函数实现时给出。 例: int add(int x=5,int y=6); void main(void) { add(); //调用在实现前 } int add(int x,int y) { return x+y; } int add(int x=5,int y=6) { return x+y; } void main(void) { add(); //调用在实现后 }
37
默认参数的优点 如果要使用的参数在函数中几乎总是采用相同的值,则默认参数非常方便 通过添加参数来增加函数的功能时,默认参数也非常有用
38
3.4 内联函数 声明形式: inline 类型说明符 被调函数名(形参表)
39
内联函数 2-1 优点 通常的函数调用会节省内存空间,但是会花费一些额外的时间(函数调用所需的时间),而内联函数不是在调用时发生控制转移,而是在编译时将函数体嵌入在调用语句处,因此节省参数传递、控制转移等开销。因此内联函数节省短函数的执行时间 inline float converter(float dollars);
40
内联函数 2-2 非常短的函数适合于内联 函数体会插入到发生函数调用的地方 fn1() fn1(); 语句; mfunc();
funcy(); main() 重复地 放到函数中的代码 放在内联函数中的代码
41
注意事项 其定义必须出现在第一次被调用前; 编译器必须先看到函数定义,而不是声明
内联函数体内一般不能有循环语句和switch语句, 通常内联函数是比较简单的函数; 其定义必须出现在第一次被调用前; 编译器必须先看到函数定义,而不是声明 inline double Cal(double r) {return * r * r;} area=Cal(r1);
42
例3-14 内联函数应用举例 #include<iostream> using namespace std;
inline double CalArea(double radius) { return 3.14*radius*radius; } int main() { double r(3.0); double area; area=CalArea(r); cout<<area<<endl; return 0;
43
函数重载 具有相同的名称,执行基本相同的操作,但是使用不同的参数列表 函数多态性
C++允许功能相近的函数在相同的作用域内以相同函数名声明,从而形成重载。方便使用,便于记忆。 具有相同的名称,执行基本相同的操作,但是使用不同的参数列表 函数多态性 void display(); void display(const char*); void display(int one, int two); void display(float number);
44
函数重载 定义:两个以上的函数,取相同的函数名,但是形参的个数或者类型不同,编绎器根据实参和形参的类型及个数的最佳匹配,自动确定调用哪一个函数,这就是函数的重载。 C++允许功能相近的函数在相同的作用域内以相同的函数名定义,从而形成重载。 int add(int x,int y); int add(int x,int y,int z); float add(float x,float y); float add(float a,float b); float add(int x,int y ) ;
45
函数重载 注意: 1、重载函数的形参必须不同(个数不同或类型不同)。若函数名相同,形参也相同,仅函数返回值类型不同,编译时会被认为是语法错误(函数重复定义)。 int add(int x,int y); int add(int a,int b); 编译器不以形参名来区分 int add(int x,int y); void add(int x,int y); 编译器不以返回值来区分
46
注意事项 2、不要将不同功能的函数定义为重载函数。以免出现调用结果的误解、混淆。这样不好: int add( int x,int y)
{ return x+y;} float add(float x,float y) {return x-y;}
47
函数重载 编译器通过调用时参数的个数和类型确定调用重载函数的哪个定义 只有对不同的数据集完成基本相同任务的函数才应重载
48
函数重载的优点 不必使用不同的函数名 有助于理解和调试代码 易于维护代码
49
数据类型不同的重载 参数的类型不同,编译器就能够区分 同一函数名输出任何数据就是重载了输出函数 int square(int);
float square(float); double square(double); 同一函数名输出任何数据就是重载了输出函数
50
参数个数不同的重载 编译器会调用参数匹配的函数 与函数的声明顺序无关 不会考虑返回类型 int square(int); //函数声明
int square(int,int,int); int asq = square(a) //函数调用 int bsq = square(x,y,z) 编译器会调用参数匹配的函数 与函数的声明顺序无关 不会考虑返回类型
51
函数重载的作用域规则 重载机制只有在函数声明的作用域内才有效 void main() class first { {
first object1; second object2; //没有发生函数重载 object1.display(1) object2.display(); } class first { public: void display(int a); }; class second void display();
52
例3-16重载函数应用举例 编写三个名为add的重载函数,分别实现两整数相加、两实数相加和两个复数相加的功能。
#include<iostream> using namespace std; struct complex { double real; double imaginary; };
53
void main(void) { int m, n; double x, y; complex c1, c2, c3; int add(int m, int n); double add(double x, double y); complex add(complex c1, complex c2); cout<<"Enter two integer: "; cin>>m>>n; cout<<"integer "<<m<<'+'<<n<<"="<<add(m,n)<<endl;
54
cout<<"Enter two real number: ";
cin>>x>>y; cout<<"real number "<<x<<'+'<<y<<"= "<<add(x,y) <<endl; cout<<"Enter the first complex number: "; cin>>c1.real>>c1.imaginary; cout<<"Enter the second complex number: "; cin>>c2.real>>c2.imaginary; c3=add(c1,c2); cout<<"complex number (" <<c1.real<< ',' << c1.imaginary <<")+("<<c2.real<<',' <<c2.imaginary<<")=("<<c3.real<<',' <<c3.imaginary<<")\n"; }
55
int add(int m, int n) { return m+n; } double add(double x, double y) { return x+y; } complex add(complex c1, complex c2) { complex c; c.real=c1.real+c2.real; c.imaginary=c1.imaginary+c2.imaginary; return c; }
56
运行结果: Enter two integer: 3 5 integer 3+5=8 Enter two real number: real number = 8.1 Enter the first complex number: Enter the second complex number: complex number (12.3,45.6)+(56.7,67.8)= (69,113.4)
57
C++系统函数 C++的系统库中提供了几百个函数可供程序员使用。 使用系统函数时要包含相应的头文件。
例如:求平方根函数(sprt)、求绝对值函数(abs)等。 使用系统函数时要包含相应的头文件。 例如:math.h 或 cmath
58
查找系统函数的使用说明 查编译系统的库函数手册 查联机帮助——VC++6.0联机帮助的使用方法:
help/Contents ->(“活动子集”栏)Visual C++ Documentation -> Visual C++ Documentation >Using Visual C > Visual C++ Programmer's Guide > Run-Time Library Reference ->Run Time Routines by Category > Run Time Routines by Category
59
总结 引用 默认参数 内联函数 函数重载
Similar presentations