C++学习——第9章 函数
1 函数的重载
利用函数的重载,可以在一个程序中使用同名的若干个函数。主要的限制是给定名称的每个函数必须有不同的参数列表。
如果满足下列条件之一,两个同名函数就是不同的:
1.1 函数的签名
函数的名称及其参数类型组合在一起,就定义了一个惟一的特性,称之为函数签名。
函数签名可以区分不同的函数,所以程序中的每个函数都必须有惟一的函数签名。只要在程序中调用函数,就必满足这个条件。
注释:
返回类型不是函数签名的一部分。实际上,这是符合逻辑的,在调用函数时,不需要存储返回的值,因此函数的返回类型就不必由调用语句确定。
代码:重载一个函数
#include <iostream>
using namespace std;double larger(double a, double b);
longlarger(long a, long b);
int main()
{double a_double = 1.5, b_double = 2.5;float a_float = 3.5f, b_float = 4.5f;long a_long = 15L, b_long = 25L;cout << endl;cout <<"The larger of double values "<<a_double<<" and "<<b_float<<" is "<<larger(a_double, b_double)<<endl;cout<<"The larger of float values"<<a_float<<" and "<<b_float<<" is "<<larger(a_float, b_float)<<endl;cout<<"The larger of long values"<<a_long<<" and "<<b_long<<" is "<<larger(a_long, b_long)<<endl;return 0;
}double larger(double a, double b)
{cout << "double larger() called" <<endl;return a>b ? a : b;
}long larger(long a, long b)
{cout << "long larger() called"<<endl;return a>b ? a : b;
}
其中,代码
cout<<"The larger of float values" <<a_float<<" and "<<b_float<<" is " <<larger(a_float, b_float)<<endl; |
调用了参数类型为float的函数larger()。我们没有定义这样的函数,但是,编译器可以把float转换为double,且没有数据丢失;于是,这是一个可接受的自动转换,编译器可以使用接受double参数的larger()版本。
1.1 重载和指针参数
由于指向不同类型的指针是不同的,因此下面的原型声明了两个不同的重载函数:
int larger(int* pValue1, int* pValue2); int larger(float* pValue1, float* pValue2); |
可以使用指向给定类型的指针作为参数。
注意:它的解释方式与该类型的数组相同。比如,int*类型的参数处理起来与int[]的参数类型相同。
下面的原型声明了相同的函数,而不是两个不同的函数:
int largest(int values[], int count); int largest(int* values, int count); |
指定这两种参数类型中的任何一种,所传送的参数都是地址,可以使用数组表示法或指针表示法来实现该函数。
1.2 重载和引用参数
不能把参数是给定类型data_type的函数,重载为参数类型是“引用data_type”的函数。否则,编译器就不能确定要调用哪个函数。
下面声明两个函数原型:
int do_it(int number); int& do_it(int& number); |
假定value的类型是int,则下面的语句:
do_it(value); |
就可以调用这两个函数中的任何一个。无法区分应调用哪个函数,因此不能根据一个版本的参数是给定类型,另一个版本的参数是该类型的引用,来区分重载函数。
代码:重载带有引用参数的函数
#include <iostream>
using namespace std;double larger(double a, double b);
long& larger(long&a, long& b);int main()
{double a_double = 1.5, b_double = 2.5;cout<<endl;cout<<"The larger of double values "<<a_double<<" and "<<b_double<<" is "<<larger(a_double, b_double)<<endl;int a_int = 35, b_int = 25;cout<<"The larger of int values "<<a_int<<" and "<<b_int<<" is "<<larger(static_cast<long>( a_int), static_cast<long>(b_int))<<endl;return 0;
}double larger(double a, double b)
{cout<<"double larger() called."<<endl;return a>b ? a : b;
}long& larger(long& a, long& b)
{cout <<"long ref larger() called."<<endl;return a>b ? a : b;
}
第二个输出结果并不是我们期望的结果,我们希望第二个输出语句调用带有long&参数的larger()版本:
cout<<"The larger of int values " <<a_int<<" and "<<b_int<<" is " <<larger(static_cast<long>( a_int), static_cast<long>(b_int)) <<endl; |
而本例中的语句调用带有double参数的函数,为什么?我们已经把两个参数都强制转换为long了!
实际上,这既是问题所在,参数不是a_int和b_int,而是包含相同值的临时位置,这两个值转换为long类型。在幕后,编译器没有准备使用临时地址来初始化引用,这太冒险了。larger()中的代码可以自由控制它对引用参数进行的操作,在理论上,两个引用参数都可以修改和返回。因为以这种方式使用临时位置不是很明智,所以编译器不使用。
处理方法有两种:1.把a_int和b_int声明为long类型。编译器就会调用参数类型为”引用long”的larger()版本。
2.如果环境不允许第一条这么做,还可以把引用参数声明为const:
long larger(const long& a, const long& b); |
一定要在函数原型和函数定义中同时进行修改,同志编译器函数不能修改参数,于是编译器就允许调用这个版本,而不是参数为double的版本。
1.1 重载和const参数
const仅能用于在定义函数签名时,区分是为引用定义参数,还是为指针定义参数。对于基本类型(如int),从重载的观点来看,const int 与 int是相同的。因此,下面的原型具有相同的函数签名,并声明同一个函数:
long& larger(long a, long b); long& larger(const long a, const long b); |
编译器会忽略第二个声明中的参数的const,这是因为参数是按值传送的,也就是说,会把每个参数的副本传送给函数,函数不会修改参数的初始值。
注意:对于任何基本类型T,参数类型是const T的函数都会解释为参数类型为T的重载函数,const会被忽略。
1.2 重载和默认参数值
对于重载函数,指定默认参数值有时会影响编译器区分函数调用的能力,产生不确定的结果。
2. 函数模板
在上面描述的一些情形中,我们编写了包括相同代码的重载函数。似乎没有必要重复编写相同的代码,最好代码只编写一次,即采用函数模板的方式。
函数模板是函数的蓝图或厨房,编译器使用它生成函数系列的新成员。
函数模板的开头是关键字template,表示这是一个模板。其后是一对尖括号,它包含了参数列表。
例如:
template<class T>T larger(T a, T b) { return a>b ? a : b; } |
单词class是一个关键字,它表示T是一个类型,但是不表示T必须是类类型。T可以使基本类型,如Int或long,也可以是用户定义的类型(也就是类)。
注释:在模板定义中,尖括号中可能会包含关键字typename,这是关键字class的另一个常见名称,与class有相同的作用。
在代码中编写模板就像编写征程的函数定义一样:
template<class T>T larger(T a, T b); |
在使用从模板中生成的函数之前,必须确保把声明(即原型)或模板的定义放在源文件中。
代码:使用函数模板
#include <iostream>
using namespace std;template<class T>T larger(T a, T b);int main()
{double a_double = 1.5, b_double = 2.5;cout<<endl;cout<<"The larger of double values "<<a_double<<" and "<<b_double<<" is "<<larger(a_double, b_double)<<endl;int a_int = 35, b_int = 25;cout<<"The larger of int values "<<a_int<<" and "<<b_int<<" is "<<larger(static_cast<long>( a_int), static_cast<long>(b_int))<<endl;return 0;
}template<class T>T larger(T a, T b)
{return a>b ? a : b;
}
3. 指针
指针存储了一个地址值。指针也可以指向函数的地址。在程序执行过程中,这种指针可以在不同的时候指向不同的函数。在程序中,总是可以使用指针来调用函数,被调用的函数地址是最近赋予指针的。
提示:函数指针在C++中没有C中那样普遍。这是因为C++提供了其他能完成类似任务的功能(类、函数重载等)。但是,函数指针在C++语言中仍有重要的地位。
3.1 声明函数指针
下面声明一个指针pfun,用于指向一个函数,该函数带有两个参数,其类型分别是long*和int,其返回值的类型是long。该声明如下所示:
long(*pfun)(long*, int); |
所有实体都加上了括号。
提示:指针名pfun的括号和星号是必须有的,没有它们,这个语句声明的就是函数,而不是指针,因为*会加在long上,而不是pfun上。
声明函数指针的一般形式:
返回类型(*指针名)(参数类型列表); |
代码:函数指针
#include<iostream>
using namespace std;long sum(long a, long b);
long product(long a, long b);int main()
{long (*pDo_it)(long, long) = 0;pDo_it = product;cout << endl<< "3*5 = "<< pDo_it(3, 5);pDo_it = sum;cout << endl<< "3*(4+5)+6= "<< pDo_it(product(3, pDo_it(4, 5)), 6);cout << endl;return 0;
}long product(long a, long b)
{return a*b;
}long sum(long a, long b)
{return a+b;
}
3.2 把函数作为参数传送
代码:传送函数指针
#include<iostream>
using namespace std;double squared(double);
double cubed(double);
double sum_array(double array[], int len, double (*pfun)(double));int main()
{double array[]={1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5};int len = sizeof array/sizeof array[0];cout << endl<< "Sum of squares = "<< sum_array(array, len, cubed)<< endl;cout << "Sum of cubes ="<< sum_array(array, len, cubed)<< endl;return 0;
}double squared(double x)
{return x*x;
}double cubed(double x)
{return x*x*x;
}double sum_array(double array[], int len, double(*pfun)(double))
{double total = 0.0;for(int i=0; i<len; i++)total += pfun(array[i]);return total;
}
3.3 函数指针的函数
声明指针数组的例子:
double sum(double, double); double product(double, double); double difference(double, double); double (*pfun[3]) (double, double) = { sum, product, difference}; |
4. 递归
代码:递归函数
#include<iostream>
#include<iomanip>
using namespace std;double power(double x, int n);int main()
{cout << endl;for(int i = -3; i <= 3; i++)cout << setw(10) << power(8.0, i);cout << endl;return 0;
}double power(double x, int n)
{if(0 == n)return 1.0;if(0 < n)return x*power(x, n-1);return 1.0/power(x, -n);
}
C++学习——第9章 函数相关推荐
- 《Go语言圣经》学习笔记 第五章函数
<Go语言圣经>学习笔记 第五章 函数 目录 函数声明 递归 多返回值 匿名函数 可变参数 Deferred函数 Panic异常 Recover捕获异常 注:学习<Go语言圣经> ...
- c语言第七章函数笔记,我的C语学习笔记-C语言教程(七).doc
我的C语学习笔记- C语言教程(七) C语言教程---第一章: C语言概论 C语言教程---第二章: 数据类型.运算符.表达式 C语言教程---第三章: C语言程序设计初步 C语言教程---第四章: ...
- c语言口令验证模块加强版,[C语言学习第3章口令验证模块的开发.ppt
[C语言学习第3章口令验证模块的开发 C语言程序 设计项目教程 第3章 口令验证模块的开发 Evaluation only. Created with Aspose.Slides for .NET 3 ...
- 《Go语言圣经》学习笔记 第九章 基于共享变量的并发
<Go语言圣经>学习笔记 第九章 基于共享变量的并发 目录 竞争条件 sync.Mutex互斥锁 syn.RWMutex读写锁 内存同步 syn.Once初始化 竞争条件检测 示例:并发的 ...
- 《Go语言圣经》学习笔记 第二章 程序结构
Go语言圣经学习笔记 第二章 程序结构 目录 命名 声明 变量 赋值 类型 包和文件 作用域 注:学习<Go语言圣经>笔记,PDF点击下载,建议看书. Go语言小白学习笔记,几乎是书上的内 ...
- 《Go语言圣经》学习笔记 第一章 Go语言入门
Go语言圣经学习笔记 第一章 Go语言入门 目录 Hello, World 命令行参数 查找重复的行 GIF动画 获取URL 并发获取多个URL Web服务 本章要点 注:学习<Go语言圣经&g ...
- 一起学习C语言:函数(四)
上一篇<一起学习C语言:函数(三)> 中,我们了解了变量的储存类别与声明方式,以及函数的递归调用原理.本章节,我们分析函数的指针调用,以及函数指针作函数参数使用的场景. 章节预览: 8. ...
- javascript进阶课程--第一章--函数
javascript进阶课程--第一章--函数 学习要点 了解内存管理 掌握全局函数的使用 知识点 基本类型和引用类型 基本类型值有:undefined,NUll,Boolean,Number和Str ...
- C++ Primer 第五版 第6章——函数阅读笔记及习题答案(完整,附C++代码)
C++Primer(第五版)第6章函数的阅读笔记及课后习题答案总结,课后习题答案是自己学习写出来的,如果有误,欢迎指正 还不完整,后续会更新添加 阅读笔记 C++ Primer 第五版 第6章 6.1 ...
最新文章
- Ajax 中XmlHttp 乱码 的解决方法 (UTF8,GB2312 编码 解码)
- ie不再询问加载java_fireFox IE刷新不提示
- Jenkins之邮件通知
- jvm十二:自定义类加载器
- 大数据如何改变安全视角
- python减法怎么表示_python运算符号之一的减法怎么用,你真的学会用python的使用方法了嘛...
- opencv入门课程:彩色图像灰度化和二值化(采用skimage库和opencv库两种方法)
- oracle12c常用新特性,开发者必读:Oracle12c新特性再总结
- c/c++成长之捷径 C/C++学习资料大全
- 中标麒麟Linux系统串口,中标麒麟操作系统串口调试方法研究-嵌入式-电子工程世界网...
- VHDL_EDA课设_八音电子琴
- 经历没有亮点可讲?你需要做份“详历”
- kafka 报错: IllegalArgumentException: Error creating broker listeners from ‘PLAINTEXT:xxx.xxx.xxx.xx
- 【读书】2020年阅读记录及心得
- 流图(程序图)表示程序的控制流——McCabe方法度量程序空间复杂度
- STM32三种BOOT模式介绍
- .c文件和.h文件之间的联系
- 计算机原理及硬件,计算机原理及硬件介绍
- ASUS华硕飞行堡垒fx80gd怎么用Fn+F5功能控制风扇转速
- mac环境下ananconda安装失败,已经设置为所有来源可安装
热门文章
- $.get、$.post、$getJSON、$ajax的用法跟区别
- 氮化镓功率芯片 NV6123、NV6113 PowerVQFN 5A
- 第十二单元 面向对象-和抽象类和对象
- 《信号完整性揭秘》读书笔记
- JavaGUI图形界面之setVisible()方法与组件显示异常总结
- flinksql-Could not acquire the minimum required resources
- Attention机制理解笔记(空间注意力+通道注意力+CBAM+BAM)
- 搜索与回溯算法:DFS(深度优先搜索)
- vs2015C++项目的安装与部署
- UTC世界标准时间、GMT格林尼治标准时间、中国标准时间北京时间