C++学习笔记(八):函数重载、函数指针和函数对象
函数重载
函数重载是指在同一作用域内,可以有一组具有相同函数名,不同参数列表的函数,这组函数被称为重载函数。重载函数通常用来命名一组功能相似的函数,这样做减少了函数名的数量,避免了名字空间的污染,对于程序的可读性有很大的好处。
- 试想如果没有函数重载机制,如在C中,你必须要这样去做:为这个print函数取不同的名字,如print_int、print_string。这里还只是两个的情况,如果是很多个的话,就需要为实现同一个功能的函数取很多个名字,如加入打印long型、char*、各种类型的数组等等。这样做很不友好!
- 类的构造函数跟类名相同,也就是说:构造函数都同名。如果没有函数重载机制,要想实例化不同的对象,那是相当的麻烦!
- 操作符重载,本质上就是函数重载,它大大丰富了已有操作符的含义,方便使用,如+可用于连接字符串等!
两个重载函数必须在下列一个或两个方面有所区别:
- 函数有不同参数。
- 函数有不同参数类型。
返回值不作为判断两个函数区别的标志。
C++运算符重载的相关规定如下:
- 不能改变运算符的优先级;
- 不能改变运算符的结合型;
- 默认参数不能和重载的运算符一起使用;
- 不能改变运算符的操作数的个数;
- 不能创建新的运算符,只有已有运算符可以被重载;
- 运算符作用于C++内部提供的数据类型时,原来含义保持不变。
总结示例:
(1)普通函数(非类成员函数)形参完全相同,返回值不同,如:
void print();
int print(); //不算重载,直接报错
(2)普通函数形参为非引用类型,非指针类型,形参一个带const,一个不带const
void print(int x);
void print(const int x); //不算重载,直接报错重定义
(3)普通函数形参为引用类型或指针类型,一个形参带const,一个不带const
void print(int *x);
void print(const int *x); //算重载,执行正确,实参为const int *时候调用这个,为int* 的时候调用上面一个 void print(int &x);
void print(const int &x); //算重载,执行正确,实参为const int &时候调用这个,为int& 的时候调用上面一个
(4)类的成员函数,形参完全相同,一个函数为const成员函数,一个函数为普通成员函数
void print();
void print() const; //算重载。const对象或const引用const指针调用时调用这个函数,普通对象或普通引用调用时调用上面一个。
函数指针
函数指针(或称为回调函数)是一个很有用也很重要的概念。当发生某种事件时,其它函数将会调用指定的函数指针指向的函数来处理特定的事件。
注意:定义的一个函数指针是一个变量。
定义一个函数指针:
两种格式:
- 返回类型 (*函数指针名称)(参数类型,参数类型,参数类型,…);
- 返回类型 (类名称::*函数成员名称)(参数类型,参数类型,参数类型,….)
示例:
int (*pFunction)(float,char,char)=NULL;//C语言的函数指针
int (MyClass::*pMemberFunction)(float,char,char)=NULL;//C++的函数指针,非静态函数成员
int (MyClass::*pConstMemberFunction)(float,char,char) const=NULL;//C++的函数指针,静态函数成员
C函数指针赋值和调用:
赋值:
int func1(float f,int a,int b){return f*a/b;}
int func2(float f,int a,int b){return f*a*b;}
//然后我们给函数指针pFunction赋值
pFunction=func1;
pFunction=&func2;
上面这段代码说明了两个问题:
- 一个函数指针可以多次赋值。
- 取地址符号是可选的,却是推荐使用的。
调用:
pFunction(10.0,’a’,’b’);
(*pFunction)(10.0,’a’,’b’);
C++类里的函数指针赋值和调用:
定义类:
MyClass
{
public:
int func1(float f,char a,char b)
{
return f*a*b;
}
int func2(float f,char a,char b) const
{
return f*a/b;
}
}
赋值:
MyClass mc;
pMemberFunction= &mc.func1;//必须要加取地址符号
pConstMemberFunction = &mc.func2;
调用:
(mc.*pMemberFunction)(10.0,’a’,’b’);
(mc.*pConstMemberFunction)(10.0,’a’,’b’);
函数指针作为参数:
#include<stdio.h>
float add(float a,float b){return a+b;}
float minus(float a,float b){return a-b;}
float multiply(float a,float b){return a*b;}
float divide(float a,float b){return a/b;}
int pass_func_pointer(float (*pFunction)(float a,float b))
{
float result=pFunction(10.0,12.0);
printf("result=%f\n",result);
}
int main()
{
pass_func_pointer(add);
pass_func_pointer(minus);
pass_func_pointer(multiply);
pass_func_pointer(divide);
return ;
}
使用函数指针作为返回值:
对于以下形式:
float (* func(char op) ) (float ,float)
其具体含义就是,声明了这样一个函数:
- 其名称为func,其参数的个数为1个;
- 其各个参数的类型为:op—char;
- 其返回变量(函数指针)类型为:float(*)(float,float)
示例:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
float add(float a,float b){return a+b;}
float minus(float a,float b){return a-b;}
float multiply(float a,float b){return a*b;}
float divide(float a,float b){return a/b;}
float(* FunctionMap(char op) )(float,float)
{
switch(op)
{
case '+':
return add;
break;
case '-':
return minus;
break;
case '*':
return multiply;
break;
case '\\':
return divide;
break;
default:
exit();
}
}
int main()
{
float a=,b=;
char ops[]={'+','-','*','\\'};
int len=strlen(ops);
int i=;
float (*returned_function_pointer)(float,float);
for(i=;i<len;i++)
{
returned_function_pointer=FunctionMap(ops[i]);
printf("the result caculated by the operator %c is %f\n",ops[i],returned_function_pointer(a,b));
}
return ;
}
使用函数指针数组:
定义一个指向函数指针类型为:float (*)(float,float)的函数指针数组,数组长度为10.正确的形式为:
float(* pFunctionArray[])(float,float)
示例:
#include<stdio.h>
float add(float a,float b){return a+b;}
float minus(float a,float b){return a-b;}
float multiply(float a,float b){return a*b;}
float divide(float a,float b){return a/b;}
int main()
{
float(*func_pointers[])(float,float)={add,minus,multiply,divide};
int i=;
float a=10.0,b=5.0;
for(i=;i<;i++)
{
printf("result is %f\n",func_pointers[i](a,b));
}
return ;
}
使用typedef进行简化:
typedef一般使用形式如下:
typedef int bool;
这在C语言中很常用,由于C语言中没有bool类型,这样定义之后可以从形式上引入一个bool类型,提高代码可读性。
然而在使用typedef定义函数指针类型的时候,和普通的使用typedef引入新类型的方式不一样。
在我们要将float (*)(float,float)类型声明为一种新类型,应该是:
typedef float(*fpType)(float,float);
这样我们就可以用fpType来表示float (*)(float,float)这种类型了,使用如下:
fpType pFunction; //在定义函数指针数组的时候可以这样定义:
fpType pFunctions[]; //在定义函数指针类型参数时可以这样定义:
void func(fpType pFunction); //在定义函数指针类型的返回值时可以这样定义:
fpType func(int a);
函数对象
函数对象实质上是一个实现了operator()括号操作符的类。
class Add
{
public:
int operator()(int a, int b)
{
return a + b;
}
};
Add add; // 定义函数对象
cout << add(,); // 5
函数指针版本是:
int AddFunc(int a, int b)
{
return a + b;
}
typedef int (*Add) (int a, int b);
Add add = &AddFunc;
cout << add(,); // 5
既然C++函数对象与函数指针在使用方式上没什么区别,那为什么要用函数对象呢?很简单,函数对象可以携带附加数据,而指针不行。
C++学习笔记(八):函数重载、函数指针和函数对象的更多相关文章
- MYSQL初级学习笔记八:MySQL中常用的函数!(视频序号:初级_45-50)
知识点十:MySQL中的函数(45-50) 数学函数: 名称 描述 CEIL() 进一取整 FLOOR() 舍一取整 MOD 取余数(取摸) POWER() 幂运算 ROUND() 四舍五入 TRUN ...
- C++学习笔记之运算符重载
一.运算符重载基本知识 在前面的一篇博文 C++学习笔记之模板(1)——从函数重载到函数模板 中,介绍了函数重载的概念,定义及用法,函数重载(也被称之为函数多态)就是使用户能够定义多个名称相同但特征标 ...
- 【opencv学习笔记八】创建TrackBar轨迹条
createTrackbar这个函数我们以后会经常用到,它创建一个可以调整数值的轨迹条,并将轨迹条附加到指定的窗口上,使用起来很方便.首先大家要记住,它往往会和一个回调函数配合起来使用.先看下他的函数 ...
- go微服务框架kratos学习笔记八 (kratos的依赖注入)
目录 go微服务框架kratos学习笔记八(kratos的依赖注入) 什么是依赖注入 google wire kratos中的wire Providers injector(注入器) Binding ...
- Go语言学习笔记八: 数组
Go语言学习笔记八: 数组 数组地球人都知道.所以只说说Go语言的特殊(奇葩)写法. 我一直在想一个人参与了两种语言的设计,但是最后两种语言的语法差异这么大.这是自己否定自己么,为什么不与之前统一一下 ...
- C++基础 学习笔记五:重载之运算符重载
C++基础 学习笔记五:重载之运算符重载 什么是运算符重载 用同一个运算符完成不同的功能即同一个运算符可以有不同的功能的方法叫做运算符重载.运算符重载是静态多态性的体现. 运算符重载的规则 重载公式 ...
- Learning ROS forRobotics Programming Second Edition学习笔记(八)indigo rviz gazebo
中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS forRobotics Pro ...
- python3.4学习笔记(八) Python第三方库安装与使用,包管理工具解惑
python3.4学习笔记(八) Python第三方库安装与使用,包管理工具解惑 许多人在安装Python第三方库的时候, 经常会为一个问题困扰:到底应该下载什么格式的文件?当我们点开下载页时, 一般 ...
- Redis学习笔记八:集群模式
作者:Grey 原文地址:Redis学习笔记八:集群模式 前面提到的Redis学习笔记七:主从复制和哨兵只能解决Redis的单点压力大和单点故障问题,接下来要讲的Redis Cluster模式,主要是 ...
- Java IO学习笔记八:Netty入门
作者:Grey 原文地址:Java IO学习笔记八:Netty入门 多路复用多线程方式还是有点麻烦,Netty帮我们做了封装,大大简化了编码的复杂度,接下来熟悉一下netty的基本使用. Netty+ ...
随机推荐
- POJ 2255 Tree Recovery 二叉树的遍历
前序和中序输入二叉树,后序输出二叉树:核心思想只有一个,前序的每个根都把中序分成了两部分,例如 DBACEGF ABCDEFG D把中序遍历的结果分成了ABC和EFG两部分,实际上,这就是D这个根的左 ...
- Git教程之删除文件(8)
在Git中,删除也是一个修改操作,我们实战一下,先添加一个新文件test.txt到Git并且提交:
- Myeclipse 8.5 优化设置
1.1 更改JavaScript文件默认编码 1.2 关闭Myeclipse不需要的启动项 1.3 取消Myeclipse自动更新 1.4 关闭Myeclipse自动验证 1.5 设置Myeclips ...
- SQL盲注修订建议
一般有多种减轻威胁的技巧: [1] 策略:库或框架 使用不允许此弱点出现的经过审核的库或框架,或提供更容易避免此弱点的构造. [2] 策略:参数化 如果可用,使用自动实施数据和代码之间的分离的结构化机 ...
- MapReduce编程系列 — 1:计算单词
1.代码: package com.mrdemo; import java.io.IOException; import java.util.StringTokenizer; import org.a ...
- Hbase二级索引
http://blog.sina.com.cn/s/blog_4a1f59bf01018apd.html
- 游戏中VIP会员模块的简单实现
哈哈 今天周末有时间,再整理一篇博文上来,虽然已经不做游戏老长时间了,但还是要把以前做过的东西总结一下,借此可以回顾以前的东西,也可以分享给大家. 今天说一下游戏中VIP会员模块的实现思路.每款游戏 ...
- multiselect2side双向选择列表插件改进版
为了满足实际程序需要,特在multiselect2side插件的基础上改进已满足异步加载(如MVC)的需要.增加了加载url.saveurl属性以及控件实时刷新加载数据方法,请注意下面红色标记部分. ...
- 深入分析Cookie的安全性问题
Cookie的目的是为用户带来方便,为网站带来增值,一般情况下不会造成严重的安全威胁.Cookie文件不能作为代码执行,也不会传送病毒,它为用户所专有并只能由创建它的服务器来读取.另外,浏览器一般只允 ...
- 如何创建,增加swap
使用分区做为SWAP比较好,速度比文件的快,也不容易产生磁盘碎片所有应该尽量使用分区作为SWAP. 先说说使用文件做为SWAP吧:一.因为做为SWAP的文件,必须是连续的,所以需要使用dd命令创建:[ ...