C++学习之普通函数指针与成员函数指针

http://blog.csdn.net/lisonglisonglisong/article/details/38353863

函数指针(function pointer)是通过指向函数的指针间接调用函数。相信很多人对指向一般函数的函数指针使用的比较多,而对指向类成员函数的函数指针则比较陌生。我最近也被问到了这方面的问题,心中却也没有清晰的答案。故决定去查阅资料,并按照自己的思路写下这篇学习总结。


一、普通函数指针

通常我们所说的函数指针指的是指向一般普通函数的指针。和其他指针一样,函数指针指向某种特定类型,所有被同一指针运用的函数必须具有相同的形参类型和返回类型。

[cpp] view plain copy
  1. int (*pf)(intint);   // 声明函数指针  

这里,pf指向的函数类型是int (int, int),即函数的参数是两个int型,返回值也是int型。注:*pf两端的括号必不可少,如果不写这对括号,则pf是一个返回值为int指针的函数。

[cpp] view plain copy
  1. #include<iostream>  
  2. #include<string>  
  3. using namespace std;  
  4.   
  5. typedef int (*pFun)(intint);  // typedef一个类型  
  6.   
  7. int add(int a, int b){  
  8.     return a+b;  
  9. }  
  10.   
  11. int mns(int a, int b){  
  12.     return a-b;  
  13. }  
  14.   
  15. string merge(const string& s1, const string& s2){  
  16.     return s1+s2;  
  17. }  
  18.   
  19. int main()  
  20. {  
  21.     pFun pf1 = add;    
  22.     cout << (*pf1)(2,3) << endl;  // 调用add函数  
  23.     pf1 = mns;  
  24.     cout << (*pf1)(8,1) << endl;  // 调用mns函数  
  25.     string (*pf2)(const string&, const string&) = merge;  
  26.     cout << (*pf2)("hello ""world") << endl;  // 调用merge函数  
  27.     return 0;  
  28. }  

如示例代码,直接声明函数指针变量显得冗长而烦琐,所以我们可以使用typedef定义自己的函数指针类型。另外,函数指针还可以作为函数的形参类型,实参则可以直接使用函数名。


二、成员函数指针

成员函数指针(member function pointer)是指可以指向类的非静态成员函数的指针。类的静态成员不属于任何对象,因此无须特殊的指向静态成员的指针,指向静态成员的指针与普通指针没有什么区别。与普通函数指针不同的是,成员函数指针不仅要指定目标函数的形参列表和返回类型,还必须指出成员函数所属的类。因此,我们必须在*之前添加classname::以表示当前定义的指针指向classname的成员函数:

[cpp] view plain copy
  1. int (A::*pf)(intint);   // 声明一个成员函数指针  
同理,这里 A::*pf 两端的括号也是必不可少的,如果没有这对括号,则pf是一个返回A类数据成员(int型)指针的函数。注意:和普通函数指针不同的是,在成员函数和指向该成员的指针之间不存在自动转换规则。

[cpp] view plain copy
  1. pf = &A::add;   // 正确:必须显式地使用取址运算符(&)  
  2. pf = A::add;    // 错误  
当我们初始化一个成员函数指针时,其指向了类的某个成员函数,但并没有指定该成员所属的对象——直到使用成员函数指针时,才提供成员所属的对象。 下面是一个成员函数指针的使用示例:

[cpp] view plain copy
  1. class A;  
  2. typedef int (A::*pClassFun)(intint);  // 成员函数指针类型  
  3.   
  4. class A{  
  5. public:  
  6.     int add(int m, int n){  
  7.         cout << m << " + " << n << " = " << m+n << endl;  
  8.         return m+n;  
  9.     }  
  10.     int mns(int m, int n){  
  11.         cout << m << " - " << n << " = " << m-n << endl;  
  12.         return m-n;  
  13.     }  
  14.     int mul(int m, int n){  
  15.         cout << m << " * " << n << " = " << m*n << endl;  
  16.         return m*n;  
  17.     }  
  18.     int dev(int m, int n){  
  19.         cout << m << " / " << n << " = " << m/n << endl;  
  20.         return m/n;  
  21.     }  
  22.   
  23.     int call(pClassFun fun, int m, int n){   // 类内部接口  
  24.         return (this->*fun)(m, n);  
  25.     }  
  26. };  
  27.   
  28. int call(A obj, pClassFun fun, int m, int n){   // 类外部接口  
  29.     return (obj.*fun)(m, n);  
  30. }  
  31.   
  32. int main()  
  33. {  
  34.     A a;  
  35.     cout << "member function 'call':" << endl;  
  36.     a.call(&A::add, 8, 4);  
  37.     a.call(&A::mns, 8, 4);  
  38.     a.call(&A::mul, 8, 4);  
  39.     a.call(&A::dev, 8, 4);  
  40.     cout << "external function 'call':" << endl;  
  41.     call(a, &A::add, 9, 3);  
  42.     call(a, &A::mns, 9, 3);  
  43.     call(a, &A::mul, 9, 3);  
  44.     call(a, &A::dev, 9, 3);  
  45.     return 0;  
  46. }  
如示例所示,我们一样可以使用 typedef 定义成员函数指针的类型别名。另外, 我们需要留意函数指针的使用方法 :对于普通函数指针,是这样使用 (*pf)(arguments) ,因为要调用函数,必须先解引用函数指针,而函数调用运算符()的优先级较高,所以 (*pf) 的括号必不可少;对于成员函数指针,唯一的不同是需要在某一对象上调用函数,所以只需要加上成员访问符即可:

[cpp] view plain copy
  1. (obj.*pf)(arguments)         // obj 是对象  
  2. (objptr->*pf)(arguments)     // objptr是对象指针   

三、函数表驱动

对于普通函数指针和指向成员函数的指针来说,一种常见的用法就是将其存入一个函数表(function table)当中。当程序需要执行某个特定的函数时,就从表中查找对应的函数指针,用该指针来调用相应的程序代码,这个就是函数指针在表驱动法中的应用。

表驱动法(Table-Driven Approach)就是用查表的方法获取信息。通常,在数据不多时可用逻辑判断语句(if…else或switch…case)来获取信息;但随着数据的增多,逻辑语句会越来越长,此时表驱动法的优势就体现出来了。

[cpp] view plain copy
  1. #include<iostream>  
  2. #include<string>  
  3. #include<map>  
  4. using namespace std;  
  5.   
  6. class A;  
  7. typedef int (A::*pClassFun)(intint);  
  8.   
  9. class A{  
  10. public:  
  11.     A(){    // 构造函数,初始化表  
  12.         table["+"] = &A::add;  
  13.         table["-"] = &A::mns;  
  14.         table["*"] = &A::mul;  
  15.         table["/"] = &A::dev;  
  16.     }  
  17.     int add(int m, int n){  
  18.         cout << m << " + " << n << " = " << m+n << endl;  
  19.         return m+n;  
  20.     }  
  21.     int mns(int m, int n){  
  22.         cout << m << " - " << n << " = " << m-n << endl;  
  23.         return m-n;  
  24.     }  
  25.     int mul(int m, int n){  
  26.         cout << m << " * " << n << " = " << m*n << endl;  
  27.         return m*n;  
  28.     }  
  29.     int dev(int m, int n){  
  30.         cout << m << " / " << n << " = " << m/n << endl;  
  31.         return m/n;  
  32.     }  
  33.     // 查找表,调用相应函数  
  34.     int call(string s, int m, int n){  
  35.         return (this->*table[s])(m, n);  
  36.     }  
  37. private:  
  38.     map<string, pClassFun> table;  // 函数表  
  39. };  
  40.   
  41. // 测试  
  42. int main()  
  43. {  
  44.     A a;  
  45.     a.call("+", 8, 2);  
  46.     a.call("-", 8, 2);  
  47.     a.call("*", 8, 2);  
  48.     a.call("/", 8, 2);  
  49.     return 0;  
  50. }  
上面是一个示例,示例中的“表”通过map来实现(当然也可以使用数组)。表驱动法使用时需要注意:一是如何查表,从表中读取正确的数据;二是表里存放什么,如数值或函数指针。




本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/383880.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HDU2683——欧拉完全数

题目要求符合等式的数&#xff0c;我们首先要做的就是分析这个数&#xff1a; 对于这个等式&#xff0c;我们可能什么都看不出来&#xff0c;左边很难化简的样子&#xff0c;所以我们就要想到通过变化怎么样把右边化成和左边形式差不多的样子。结合组合数我们想到二项式定理&am…

BZOJ-2005能量采集-数论函数

很入门的数论函数题目。我还是wa了一发&#xff08;爆long long 了&#xff09; 对于每个位置x,y&#xff0c;在他们和能量采集器中间的植物为gcd(x,y)-1&#xff0c;【在他们之间说明斜率相同&#xff0c;而和他们斜率相同的就是所有gcd(x/gcd(x,y),y/gcd(x,y))1的并且比他们小…

网络五层模型

TCP/IP五层模型 应用层: HTTP,HTTPS协议,其中HTTP没有对数据进行加密操作,但是HTTPS对数据进行了加密操作 其中HTTP端口号一般是80/8080等等,HTTPS端口号是443,SSH端口号一般是22,ftp是21 HTTP协议报头: 首行:请求方法,url,协议版本 请求报头: HOST:主机 Connection:长连接…

C++的静态成员函数指针

http://blog.csdn.net/sky453589103/article/details/47276789 先简单的说说非静态的成员函数。非静态成员函数指针的类型&#xff1a;类的非静态成员是和类的对象相关的。也就是说&#xff0c;要通过类的对象来访问变量。成员函数的类型定义为&#xff1a;typedef void (A::*p…

从一个字符串中删除另一个字符串中出现过的字符

http://blog.csdn.net/walkerkalr/article/details/39001155 定义一个函数&#xff0c;输入两个字符串&#xff0c;从第一个字符串中删除在第二个中出现过的所偶字符串。例如从第一个字符串"We are students."中删除第二个字符中“auiou”中出现过的字符得到的结果是…

SPOJ-VLATTICE Visible Lattice Points-莫比乌斯反演

需要将问题分解一下。 我们需要求的是这个立方体从(0,0,0)能看到的点的个数。可是在三个含有(0,0,0)的面上我们没有办法和其他的一起进行分析&#xff08;因为含有坐标是0&#xff0c;而我们的数论工具都是从1开始的&#xff09;&#xff0c;所以我们可以将那三个面分开考虑&a…

进程的通信

管道 什么是管道 由于进程之间是相互独立的,因此在进程与进程之间进行相互联系的时候,此时就需要采用一种机制,通过管道的方式将进程一个进程的执行数据交给另外一个进程,此时就可以以通过管道来实现 匿名管道和命名管道 匿名管道 创建一个匿名管道的方式是pipe(int pipe[…

获取网络接口信息——ioctl()函数与结构体struct ifreq、 struct ifconf

http://blog.csdn.net/windeal3203/article/details/39320605 Linux 下 可以使用ioctl()函数 以及 结构体 struct ifreq 结构体struct ifconf来获取网络接口的各种信息。 ioctl 首先看ioctl()用法ioctl()原型如下&#xff1a;#include <sys/ioctl.h>int ioctl(int fd, i…

欧拉降幂

我们记f(n)为n的欧拉函数值&#xff0c;则 当B>f©时&#xff0c;AB%CAB%f©f©%C&#xff0c;这里A,C可能不互质。 很好用&#xff0c;证明很复杂&#xff0c;等有时间回来学习一下。

房多多面试总结

测试一个ATM机 功能上 取钱     正常         要取的钱的面值是否支持ATM机服务         要取的钱的数目是否小于等于存的钱的数目         要取的钱的数目大于存钱的数目     异常         取钱的时候操作出现异常&#xff0c;导致…

BZOJ3884上帝与集合的正确用法-欧拉函数

刚开始我想的是欧拉降幂&#xff0c;可是觉得复杂度还是挺高的就去找了一下题解。 思路大方向没有问题&#xff0c;仍然是使用欧拉函数降低指数然后递归处理。但是不是简单的使用欧拉降幂而是应该对模数p稍微处理一下。因为底数已经确定为2&#xff0c;所以我们可以将p写成p2k…

比较ArrayList和数组的区别

区别1:创建时的区别 一般数组在创建的时候都需要指定数组的大小&#xff0c;但是ArrayList不需要指定数组的大小 //创建一个ArrayList对象 ArrayList<String> myList new ArrayList<String>(); //创建一个数组 String [] myList new String[2];区别2&#xff1…

linux C如何获取服务器节点上所有网口的ip地址

http://blog.csdn.net/weiyuefei/article/details/22198659 之前项目原因&#xff0c;需要获取当前服务器节点上所有网口的ip地址&#xff0c;但是当时由于时间比较紧&#xff0c;一直没搞出来&#xff0c;最近没那么忙了&#xff0c;又在网上找了一下&#xff0c;终于实现了这…

HDU1573-模线性方程

模线性方程的模板题。&#xff08;卡了一会&#xff0c;发现读入弄错了&#xff09; #include<cstdio> #include<cstring> #include<algorithm> #include<climits> #include<cmath> #include<cstdlib> #include<ctime> #include<…

java中引用传递

基本概念 栈内存 所谓的栈内存就是存储进程在运行过程中变量的内存空间 堆内存 所谓的堆内存就是存储系统中数据的内存空间 数组相关的引用传递 先来看一段代码 public class ArrayDemo {public static void main(String[] args) {int[] x null;x new int[3];System.o…

(原创)C++11改进我们的程序之右值引用

http://www.cnblogs.com/qicosmos/p/3369940.html 本次主要讲c11中的右值引用&#xff0c;后面还会讲到右值引用如何结合std::move优化我们的程序。 c11增加了一个新的类型&#xff0c;称作右值引用(R-value reference)&#xff0c;标记为T &&&#xff0c;说到右值引用…

(原创)C++11改进我们的程序之move和完美转发

http://www.cnblogs.com/qicosmos/p/3376241.html 本次要讲的是右值引用相关的几个函数&#xff1a;std::move, std::forward和成员的emplace_back&#xff0c;通过这些函数我们可以避免不必要的拷贝&#xff0c;提高程序性能。move是将对象的状态或者所有权从一个对象转移到另…

微型个人博客服务器

Http相关简介 Http是应用层的基于请求响应的一个协议, 其中Http的请求响应可以分为四部分. 请求行, 请求报头,空行, 请求正文.其中请求行包括了请求方法, url, 版本号, 请求报头包括请求属性, 冒分割的键值对, 每组属性之间都以换行的形式分开, 最后一空行作为请求的结束标识.…

HDU6428-Calculate-数论函数

并不知道为什么同样一份代码早上超时下午就A了…好像数据是随机的? 做的第一道不是简单板题的数论函数题.果然做不出来… 在网上研究了好久,才算稍微研究明白.看到了两种推导的思路.(写了半天发现讲起来好麻烦,有时间再来更新) #include<cstdio> #include<cstring&g…

[C/C++]关于C++11中的std::move和std::forward

http://blog.sina.com.cn/s/blog_53b7ddf00101p5t0.htmlstd::move是一个用于提示优化的函数&#xff0c;过去的c98中&#xff0c;由于无法将作为右值的临时变量从左值当中区别出来&#xff0c;所以程序运行时有大量临时变量白白的创建后又立刻销毁&#xff0c;其中又尤其是返回…