C++学习笔记(八):函数重载、函数指针和函数对象

函数重载

函数重载是指在同一作用域内,可以有一组具有相同函数名,不同参数列表的函数,这组函数被称为重载函数。重载函数通常用来命名一组功能相似的函数,这样做减少了函数名的数量,避免了名字空间的污染,对于程序的可读性有很大的好处。

  1. 试想如果没有函数重载机制,如在C中,你必须要这样去做:为这个print函数取不同的名字,如print_int、print_string。这里还只是两个的情况,如果是很多个的话,就需要为实现同一个功能的函数取很多个名字,如加入打印long型、char*、各种类型的数组等等。这样做很不友好!
  2. 类的构造函数跟类名相同,也就是说:构造函数都同名。如果没有函数重载机制,要想实例化不同的对象,那是相当的麻烦!
  3. 操作符重载,本质上就是函数重载,它大大丰富了已有操作符的含义,方便使用,如+可用于连接字符串等!

两个重载函数必须在下列一个或两个方面有所区别:

  1. 函数有不同参数。
  2. 函数有不同参数类型。

返回值不作为判断两个函数区别的标志。

C++运算符重载的相关规定如下:

  1. 不能改变运算符的优先级;
  2. 不能改变运算符的结合型;
  3. 默认参数不能和重载的运算符一起使用;
  4. 不能改变运算符的操作数的个数;
  5. 不能创建新的运算符,只有已有运算符可以被重载;
  6. 运算符作用于C++内部提供的数据类型时,原来含义保持不变。

总结示例:

(1)普通函数(非类成员函数)形参完全相同,返回值不同,如:

1 void print();
2 int print(); //不算重载,直接报错

(2)普通函数形参为非引用类型,非指针类型,形参一个带const,一个不带const

1 void print(int x);
2 void print(const int x); //不算重载,直接报错重定义

(3)普通函数形参为引用类型或指针类型,一个形参带const,一个不带const

1 void print(int *x);
2 void print(const int *x); //算重载,执行正确,实参为const int *时候调用这个,为int* 的时候调用上面一个
3
4 void print(int &x);
5 void print(const int &x); //算重载,执行正确,实参为const int &时候调用这个,为int& 的时候调用上面一个

(4)类的成员函数,形参完全相同,一个函数为const成员函数,一个函数为普通成员函数

1 void print();
2 void print() const; //算重载。const对象或const引用const指针调用时调用这个函数,普通对象或普通引用调用时调用上面一个。

函数指针

函数指针(或称为回调函数)是一个很有用也很重要的概念。当发生某种事件时,其它函数将会调用指定的函数指针指向的函数来处理特定的事件。

注意:定义的一个函数指针是一个变量。

定义一个函数指针:

两种格式:

  1. 返回类型 (*函数指针名称)(参数类型,参数类型,参数类型,…);
  2. 返回类型 (类名称::*函数成员名称)(参数类型,参数类型,参数类型,….)

示例:

1 int (*pFunction)(float,char,char)=NULL;//C语言的函数指针
2 int (MyClass::*pMemberFunction)(float,char,char)=NULL;//C++的函数指针,非静态函数成员
3 int (MyClass::*pConstMemberFunction)(float,char,char) const=NULL;//C++的函数指针,静态函数成员

C函数指针赋值和调用:

赋值:

1 int func1(float f,int a,int b){return f*a/b;}
2 int func2(float f,int a,int b){return f*a*b;}
3 //然后我们给函数指针pFunction赋值
4 pFunction=func1;
5 pFunction=&func2;

上面这段代码说明了两个问题:

  1. 一个函数指针可以多次赋值。
  2. 取地址符号是可选的,却是推荐使用的。

调用:

1 pFunction(10.0,’a’,’b’);
2 (*pFunction)(10.0,’a’,’b’);

C++类里的函数指针赋值和调用:

定义类:

 1 MyClass
 2 {
 3 public:
 4     int func1(float f,char a,char b)
 5     {
 6         return f*a*b;
 7     }
 8     int func2(float f,char a,char b) const
 9     {
10         return f*a/b;
11     }
12 }

赋值:

1 MyClass mc;
2 pMemberFunction= &mc.func1;//必须要加取地址符号
3 pConstMemberFunction = &mc.func2;

调用:

1 (mc.*pMemberFunction)(10.0,’a’,’b’);
2 (mc.*pConstMemberFunction)(10.0,’a’,’b’);

函数指针作为参数:

 1 #include<stdio.h>
 2
 3 float add(float a,float b){return a+b;}
 4
 5 float minus(float a,float b){return a-b;}
 6
 7 float multiply(float a,float b){return a*b;}
 8
 9 float divide(float a,float b){return a/b;}
10
11 int pass_func_pointer(float (*pFunction)(float a,float b))
12 {
13       float result=pFunction(10.0,12.0);
14       printf("result=%f\n",result);
15 }
16
17 int main()
18 {
19       pass_func_pointer(add);
20       pass_func_pointer(minus);
21       pass_func_pointer(multiply);
22       pass_func_pointer(divide);
23       return 0;
24 } 

使用函数指针作为返回值:

对于以下形式:

1 float (* func(char op) ) (float ,float)

其具体含义就是,声明了这样一个函数:

  1. 其名称为func,其参数的个数为1个;
  2. 其各个参数的类型为:op—char;
  3. 其返回变量(函数指针)类型为:float(*)(float,float)

示例:

 1 #include<stdio.h>
 2
 3 #include<stdlib.h>
 4
 5 #include<string.h>
 6
 7 float add(float a,float b){return a+b;}
 8
 9 float minus(float a,float b){return a-b;}
10
11 float multiply(float a,float b){return a*b;}
12
13 float divide(float a,float b){return a/b;}
14
15
16
17 float(* FunctionMap(char op) )(float,float)
18
19 {
20
21       switch(op)
22
23       {
24
25              case ‘+‘:
26
27                     return add;
28
29                     break;
30
31              case ‘-‘:
32
33                     return minus;
34
35                     break;
36
37              case ‘*‘:
38
39                     return multiply;
40
41                     break;
42
43              case ‘\\‘:
44
45                     return divide;
46
47                     break;
48
49              default:
50
51                     exit(1);
52
53       }
54
55 }
56
57
58
59 int main()
60
61 {
62
63       float a=10,b=5;
64
65       char ops[]={‘+‘,‘-‘,‘*‘,‘\\‘};
66
67       int len=strlen(ops);
68
69       int i=0;
70
71       float (*returned_function_pointer)(float,float);
72
73       for(i=0;i<len;i++)
74
75       {
76
77              returned_function_pointer=FunctionMap(ops[i]);
78
79              printf("the result caculated by the operator %c is %f\n",ops[i],returned_function_pointer(a,b));
80
81       }
82
83       return 0;
84
85 } 

使用函数指针数组:

定义一个指向函数指针类型为:float (*)(float,float)的函数指针数组,数组长度为10.正确的形式为:

1 float(* pFunctionArray[10])(float,float)

示例:

 1 #include<stdio.h>
 2
 3 float add(float a,float b){return a+b;}
 4
 5 float minus(float a,float b){return a-b;}
 6
 7 float multiply(float a,float b){return a*b;}
 8
 9 float divide(float a,float b){return a/b;}
10
11 int main()
12
13 {
14       float(*func_pointers[4])(float,float)={add,minus,multiply,divide};
15       int i=0;
16       float a=10.0,b=5.0;
17
18       for(i=0;i<4;i++)
19       {
20              printf("result is %f\n",func_pointers[i](a,b));
21       }
22       return 0;
23 } 

使用typedef进行简化:

typedef一般使用形式如下:

1 typedef int bool;

这在C语言中很常用,由于C语言中没有bool类型,这样定义之后可以从形式上引入一个bool类型,提高代码可读性。

然而在使用typedef定义函数指针类型的时候,和普通的使用typedef引入新类型的方式不一样。

在我们要将float (*)(float,float)类型声明为一种新类型,应该是:

1 typedef float(*fpType)(float,float);

这样我们就可以用fpType来表示float (*)(float,float)这种类型了,使用如下:

 1 fpType pFunction;
 2
 3 //在定义函数指针数组的时候可以这样定义:
 4 fpType pFunctions[10];
 5
 6 //在定义函数指针类型参数时可以这样定义:
 7 void func(fpType pFunction);
 8
 9 //在定义函数指针类型的返回值时可以这样定义:
10 fpType func(int a);

函数对象

函数对象实质上是一个实现了operator()括号操作符的类。

 1 class Add
 2 {
 3 public:
 4 int operator()(int a, int b)
 5 {
 6 return a + b;
 7 }
 8 };
 9 Add add; // 定义函数对象
10 cout << add(3,2); // 5 

函数指针版本是:

1 int AddFunc(int a, int b)
2 {
3 return a + b;
4 }
5 typedef int (*Add) (int a, int b);
6 Add add = &AddFunc;
7 cout << add(3,2); // 5 

既然C++函数对象与函数指针在使用方式上没什么区别,那为什么要用函数对象呢?很简单,函数对象可以携带附加数据,而指针不行。

时间: 2024-12-14 20:18:51

C++学习笔记(八):函数重载、函数指针和函数对象的相关文章

C++ Primer Plus学习笔记之运算符重载

C++ Primer Plus学习笔记之运算符重载 1,成员函数和友元函数选择的建议 下面我们先看两个例子: 成员函数重载 #include<iostream> using namespace std; class Complex { public: Complex(double r=0,double i=0) { re=r; im=i; } Complex operator+(const Complex& obj); Complex operator!(); void Display

C++ Primer 学习笔记_26_操作符重载与转换(1)--可重载/不可重载的操作符、成员函数方式重载、友元函数方式重载

C++ Primer 学习笔记_26_操作符重载与转换(1)--可重载/不可重载的操作符.成员函数方式重载.友元函数方式重载 引言: 明智地使用操作符重载可以使类类型的使用像内置类型一样直观! 一.重载的操作符名 像任何其他函数一样,操作符重载函数有一个返回值和一个形参表.形参表必须具有操作符数目相同的形参.比如赋值时二元运算,所以该操作符函数有两个参数:第一个形参对应着左操作数,第二个形参对应右操作数. 大多数操作符可以定义为成员函数或非成员函数.当操作符为成员函数时,它的第一个操作数隐式绑定

C++ Primer 学习笔记_28_操作符重载与转换(3)--成员函数的重载、覆盖与隐藏、类型转换运算符、*运算符重载、-&gt;运算符重载

C++ Primer 学习笔记_28_操作符重载与转换(3)--成员函数的重载.覆盖与隐藏.类型转换运算符.*运算符重载.->运算符重载 一.成员函数的重载.覆盖与隐藏 对于类层次的同名成员函数来说,有三种关系:重载.覆盖和隐藏,理清3种关系,有助于写出高质量的代码. 1.成员函数的重载 重载的概念相对简单,只有在同一类定义中的同名成员函数才存在重载关系,主要特点时函数的参数类型和数目有所不同:但不能出现函数参数的个数和类型均相同,仅仅依靠返回值类型不同来区分的函数,这和普通函数的重载是完全一致

C++学习笔记之模板(1)——从函数重载到函数模板

一.函数重载 因为函数重载比较容易理解,并且非常有助于我们理解函数模板的意义,所以这里我们先来用一个经典的例子展示为什么要使用函数重载,这比读文字定义有效的多. 现在我们编写一个交换两个int变量值得函数,可以这样写: 1 void swap(int & a, int & b) 2 { 3 int tmp; 4 5 tmp = a; 6 a = b; 7 b = tmp; 8 9 } 假如我们现在想在与上面函数相同的文件中(可以理解为同一个main函数中)交换两个float变量呢,是不是需

C++ Primer 学习笔记_29_操作符重载与转换(4)--转换构造函数和类型转换运算符归纳、operator new 和 operator delete 实现一个简单内存泄漏跟踪器

C++ Primer 学习笔记_29_操作符重载与转换(4)--转换构造函数和类型转换运算符归纳.operator new 和 operator delete 实现一个简单内存泄漏跟踪器 一.转换构造函数 可以用单个实参来调用的构造函数定义从形参类型到该类型的一个隐式转换.如下: class Integral { public: Integral (int = 0); //转换构造函数 private: int real; }; Integral A = 1; //调用转换构造函数将1转换为In

Lua学习笔记(八):数据结构

table是Lua中唯一的数据结构,其他语言所提供的数据结构,如:arrays.records.lists.queues.sets等,Lua都是通过table来实现,并且在Lua中table很好的实现了这些数据结构. 1.数组 在Lua中通过整数下标访问table中元素,既是数组,并且数组大小不固定,可动态增长.通常我们初始化数组时,就间接地定义了数组的大小,例如: 1 a = {} -- new array 2 for i=1, 1000 do 3 a[i] = 0 4 end 5 6 --数

C++学习笔记之运算符重载

一.运算符重载基本知识 在前面的一篇博文 C++学习笔记之模板(1)——从函数重载到函数模板 中,介绍了函数重载的概念,定义及用法,函数重载(也被称之为函数多态)就是使用户能够定义多个名称相同但特征标(参数列表)不同的函数,目的是在对不同类型的参数执行相同的操作时只用一个同名的函数. 运算符重载,就是使同一个运算符在面临不同类型的数据时作出不同的操作(函数重载是操作相同),就是让同一个运算符有多重功能.实际上我们经常用的许多运算符已被重载,例如,将*用于地址,将得到存储在这个地址中的值:但将它用

Linux System Programming 学习笔记(八) 文件和目录管理

1. 文件和元数据 每个文件都是通过inode引用,每个inode索引节点都具有文件系统中唯一的inode number 一个inode索引节点是存储在Linux文件系统的磁盘介质上的物理对象,也是LInux内核通过数据结构表示的实体 inode存储相关联文件的元数据 ls -i 命令获取文件的inode number /* obtaining the metadata of a file */ #include <sys/types.h> #include <sys/stat.h>

C++ Primer 学习笔记_27_操作符重载与转换(2)--++/--运算符重载、!运算符重载、赋值运算符重载 、String类([]、 +、 += 运算符重载)、&gt;&gt;和&lt;&lt;运算符重载

C++ Primer 学习笔记_27_操作符重载与转换(2)--++/--运算符重载.!运算符重载.赋值运算符重载 .String类([]. +. += 运算符重载).>>和<<运算符重载 一.++/--运算符重载 1.前置++运算符重载 成员函数的方式重载,原型为: 函数类型 & operator++(); 友元函数的方式重载,原型为: friend 函数类型 & operator++(类类型 &); 2.后置++运算符重载 成员函数的方式重载,原型为:

Java学习笔记之方法重载,动态方法调度和抽象类

一.方法重载 如果子类中的方法与它的超类中的方法有相同的方法名,则称子类中的方法重载超类中的方法,特别是当超类和子类中的方法名和参数类型都相同时,在子类中调用该方法时,超类中的方法会被隐藏.考虑下面程序: 1 class A 2 { 3 int i, j; 4 A(int a, int b) 5 { 6 i = a; 7 j = b; 8 } 9 10 // display i and j 11 void show() 12 { 13 System.out.println("i and j: &