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章 函数相关推荐

  1. 《Go语言圣经》学习笔记 第五章函数

    <Go语言圣经>学习笔记 第五章 函数 目录 函数声明 递归 多返回值 匿名函数 可变参数 Deferred函数 Panic异常 Recover捕获异常 注:学习<Go语言圣经> ...

  2. c语言第七章函数笔记,我的C语学习笔记-C语言教程(七).doc

    我的C语学习笔记- C语言教程(七) C语言教程---第一章: C语言概论 C语言教程---第二章: 数据类型.运算符.表达式 C语言教程---第三章: C语言程序设计初步 C语言教程---第四章: ...

  3. c语言口令验证模块加强版,[C语言学习第3章口令验证模块的开发.ppt

    [C语言学习第3章口令验证模块的开发 C语言程序 设计项目教程 第3章 口令验证模块的开发 Evaluation only. Created with Aspose.Slides for .NET 3 ...

  4. 《Go语言圣经》学习笔记 第九章 基于共享变量的并发

    <Go语言圣经>学习笔记 第九章 基于共享变量的并发 目录 竞争条件 sync.Mutex互斥锁 syn.RWMutex读写锁 内存同步 syn.Once初始化 竞争条件检测 示例:并发的 ...

  5. 《Go语言圣经》学习笔记 第二章 程序结构

    Go语言圣经学习笔记 第二章 程序结构 目录 命名 声明 变量 赋值 类型 包和文件 作用域 注:学习<Go语言圣经>笔记,PDF点击下载,建议看书. Go语言小白学习笔记,几乎是书上的内 ...

  6. 《Go语言圣经》学习笔记 第一章 Go语言入门

    Go语言圣经学习笔记 第一章 Go语言入门 目录 Hello, World 命令行参数 查找重复的行 GIF动画 获取URL 并发获取多个URL Web服务 本章要点 注:学习<Go语言圣经&g ...

  7. 一起学习C语言:函数(四)

    上一篇<一起学习C语言:函数(三)> 中,我们了解了变量的储存类别与声明方式,以及函数的递归调用原理.本章节,我们分析函数的指针调用,以及函数指针作函数参数使用的场景. 章节预览: 8. ...

  8. javascript进阶课程--第一章--函数

    javascript进阶课程--第一章--函数 学习要点 了解内存管理 掌握全局函数的使用 知识点 基本类型和引用类型 基本类型值有:undefined,NUll,Boolean,Number和Str ...

  9. C++ Primer 第五版 第6章——函数阅读笔记及习题答案(完整,附C++代码)

    C++Primer(第五版)第6章函数的阅读笔记及课后习题答案总结,课后习题答案是自己学习写出来的,如果有误,欢迎指正 还不完整,后续会更新添加 阅读笔记 C++ Primer 第五版 第6章 6.1 ...

最新文章

  1. Ajax 中XmlHttp 乱码 的解决方法 (UTF8,GB2312 编码 解码)
  2. ie不再询问加载java_fireFox IE刷新不提示
  3. Jenkins之邮件通知
  4. jvm十二:自定义类加载器
  5. 大数据如何改变安全视角
  6. python减法怎么表示_python运算符号之一的减法怎么用,你真的学会用python的使用方法了嘛...
  7. opencv入门课程:彩色图像灰度化和二值化(采用skimage库和opencv库两种方法)
  8. oracle12c常用新特性,开发者必读:Oracle12c新特性再总结
  9. c/c++成长之捷径 C/C++学习资料大全
  10. 中标麒麟Linux系统串口,中标麒麟操作系统串口调试方法研究-嵌入式-电子工程世界网...
  11. VHDL_EDA课设_八音电子琴
  12. 经历没有亮点可讲?你需要做份“详历”
  13. kafka 报错: IllegalArgumentException: Error creating broker listeners from ‘PLAINTEXT:xxx.xxx.xxx.xx
  14. 【读书】2020年阅读记录及心得
  15. 流图(程序图)表示程序的控制流——McCabe方法度量程序空间复杂度
  16. STM32三种BOOT模式介绍
  17. .c文件和.h文件之间的联系
  18. 计算机原理及硬件,计算机原理及硬件介绍
  19. ASUS华硕飞行堡垒fx80gd怎么用Fn+F5功能控制风扇转速
  20. mac环境下ananconda安装失败,已经设置为所有来源可安装

热门文章

  1. $.get、$.post、$getJSON、$ajax的用法跟区别
  2. 氮化镓功率芯片 NV6123、NV6113 PowerVQFN 5A
  3. 第十二单元 面向对象-和抽象类和对象
  4. 《信号完整性揭秘》读书笔记
  5. JavaGUI图形界面之setVisible()方法与组件显示异常总结
  6. flinksql-Could not acquire the minimum required resources
  7. Attention机制理解笔记(空间注意力+通道注意力+CBAM+BAM)
  8. 搜索与回溯算法:DFS(深度优先搜索)
  9. vs2015C++项目的安装与部署
  10. UTC世界标准时间、GMT格林尼治标准时间、中国标准时间北京时间