- VisualStudio2022插件的安装及使用-编程手把手系列文章
- pprof-在现网场景怎么用
- C#实现的下拉多选框,下拉多选树,多级节点
- 【学习笔记】基础数据结构:猫树
本文详细介绍了C/C++中的普通函数和类的成员函数的指针。结合C++代码示例讲解了函数指针作为其他函数的输入、返回值以及typedef如何提高代码可读性的实用技巧。对于类的成员函数(方法)指针,则分为静态和非静态两种情况。最后总结了普通函数、类的非静态成员函数、类的静态成员函数的声明、赋值和定义的C++语法以供查阅.
首先让我们来区别以下4条声明语句:
int x; // x是一个int型的变量
int *x; // x是一个指向int型变量的指针
int *x(); // x是一个返回int型指针的函数
int (*x)(); // x是一个函数指针,输入参数为空,返回类型为int型指针
因此,把一个名为fun的函数的声明语句变为函数指针变量pfun的声明语句,只需要将 fun 变为 (*pfun),其他的不变即可(注意:小括号必不可少!).
double fun(string& str1, string &str2); // 函数fun的声明
double (*pfun)(string& str1, string &str2); // 函数指针变量pfun的声明
在C/C++中,数组变量的名字是一个指向该数组元素类型的指针常量(存放首元素的地址),类似地,函数的名字也表示一个指向该函数的指针常量(存放该函数入口地址):
int array[5]; // array是一个指向int的指针常量
int *p = array; // p是一个指向int的指针变量,p = array合法
p[3]; // 等价于array[3]
void test(int a){ cout << "a=" << a << endl;} // test是一个函数指针常量,输入参数int,返回类型void
void (*pf)(int); // 声明函数指针pf: pf是一个输入参数int,返回void的函数的指针
pf = test; // 将test函数地址赋值给pf(二者输入参数列表、返回类型必须一致)
pf(123); // 等价于test(123)
有几点需要说明:
// 声明和赋值可以写在一起:
void (*pf)(int) = test;
// 2种赋值方式:(等价)
pf = test; // 直接函数名赋值
pf = &test; // 函数名取地址赋值,等价于pf = test
// 2种调用方式:(等价)
pf(123); // 直接pf接参数调用
(*pf)(123); // 先对函数指针解引用,再接参数调用,等价于pf(123)
例1:
#include <iostream>
using namespace std;
int add(int x, int y){
return x + y;
}
int substract(int x, int y){
return x - y;
}
int main(){
int (*fp)(int, int); // 定义函数指针变量:fp
fp = add; // 将同类型函数add赋值给fp
cout << fp(1, 3) << endl;
cout << (*fp)(1, 3) << endl; // 等价于fp(1,3)
fp = &substract; // 将同类型函数substract赋值给fp, 取地址符号"&"可加可不加
cout << fp(1, 3) << endl;
cout << (*fp)(1, 3) << endl; // 等价于fp(1,3)
return 0;
}
输出:
4
4
-2
-2
如果有同名的重载函数,编译器会自动选择类型匹配的一个:
#include <iostream>
using namespace std;
int add(int x, int y){
return x + y;
}
int add(int x, int y, int z){ // 重载
return x + y + z;
}
int main(){
int (*fp)(int, int) = add; // 编译器会自动选择add(int x, int y)与fp匹配
cout << "1 + 2 = " << fp(1,2) << endl;
int (*fp2)(int, int, int) = add; // 编译器会自动选择add(int x, int y, int z)与fp2匹配
cout << "1 + 2 + 3 = " << fp2(1,2,3) << endl;
return 0;
}
输出:
1 + 2 = 3
1 + 2 + 3 = 6
当一个函数的输入列表、返回列表较为复杂时,尤其是参数/返回值中还嵌套了函数指针时,代码的可读性将会大大下降.
函数指针作为输入参数(形参)有两种写法:将输入参数int变为int (*pf)(int, int),表示输入参数为函数指针pf(指向函数类型为int (*pf)(int, int)).
例2:下面例子中的add2()和add3() 就是输入参数为函数声明/函数指针的两种等价写法.
#include <iostream>
using namespace std;
int add(int x, int y){ // 双参数函数add2:两个数相加
return x + y;
}
int add2(int (*fun)(int, int), int x, int y, int z){ // 使用函数声明语句作为形参,构造三参数函数add2()
return fun(fun(x,y),z);
}
int add3(int fun(int, int), int x, int y, int z){ // 与上面等价, 使用函数指针作为形参,构造三参数函数add3()
return fun(fun(x,y),z);
}
int main(){
int (*fp)(int, int);
fp = add;
cout << "1 + 2 = " << fp(1,2) << endl;
int (*fp2)(int (*fun)(int, int), int, int, int); // 第一个参数是双参数函数指针,第二到四个参数为int
fp2 = add2; // fp2赋值为类型一致的函数名
cout << "1 + 2 + 3 = " << fp2(fp,1,2,3) << endl;
fp2 = add3; // 与上面等价,add3和add2实际是一样的,都能被fp2接收
cout << "4 + 5 + 6 = " << fp2(fp,4,5,6) << endl;
return 0;
}
输出结果:
1 + 2 = 3
1 + 2 + 3 = 6
4 + 5 + 6 = 15
函数指针作为返回值的写法:只需要将int fun(int)变成 int (*fun(int))(double, double), 表示fun(int)返回一个函数指针pf(指向类型为int (*pf)(double, double)).
例3:无参数输入的函数createAdd()函数返回一个函数指针:
#include <iostream>
using namespace std;
int add(int x, int y){
return x + y;
}
// 定义一个名为createAdd()的函数
// 输入参数为空
// 返回值为一个函数指针pTemp(其类型为int (*pTemp)(int, int))
int (*createAdd())(int, int){
return add;
}
int main(){
int (*fp)(int, int); // 声明一个函数指针fp
fp = createAdd(); // fp接收createAdd()返回值,二者都是同类型的函数指针,匹配
cout << "1 + 2 = " << fp(1,2) << endl;
return 0;
}
输出:
1 + 2 = 3
例4:带参数输入的createAlgorithm(int type)函数返回一个函数指针:
#include <iostream>
using namespace std;
// 定义三种运算:空运算、加法、减法
int none(int , int ){
return 0;
}
int add(int x, int y){
return x + y;
}
int substract(int x, int y){
return x - y;
}
// 定义一个名为createAlgorithm(int)的函数:
// 输入参数为int,表示选择哪一种运算符(1为加法,2为减法)
// 返回值为对应运算符的函数指针pTemp(其类型为int (*pTemp)(int, int))
int (*createAlgorithm(int type))(int, int){
switch(type){
case 1:
return add;
break;
case 2:
return substract;
break;
default:
return none;
}
}
int main(){
int (*fp)(int, int); // 声明一个函数指针fp
fp = createAlgorithm(1); // fp接收createAlgorithm(1)返回值,二者都是同类型的函数指针,匹配
cout << "1 + 2 = " << fp(1,2) << endl;
fp = createAlgorithm(2); // fp接收createAlgorithm(2)返回值,二者都是同类型的函数指针,匹配
cout << "5 - 1 = " << fp(5,1) << endl;
return 0;
}
输出:
1 + 2 = 3
5 - 1 = 4
例5:输入参数和返回参数都含有函数指针:(int (calculateAndTransfer(int (fun)(int, int), int x, int y))(int, int){...}) 。
#include <iostream>
using namespace std;
int add(int x, int y){
return x + y;
}
int substract(int x, int y){
return x - y;
}
// 定义一个名为calculateAndTransfer(int)的函数:在内部执行一次计算,并且把输入的函数指针再返回,传递下去
// 输入参数为函数指针(其类型为int (*pTemp)(int, int))和两个int变量
// 返回值为同样的函数指针pTemp(其类型为int (*pTemp)(int, int))
int (*calculateAndTransfer(int (*fun)(int, int), int x, int y))(int, int){
cout << "fun(x,y) = " << fun(x,y) << endl;
return fun;
}
int main(){
int (*fp)(int, int);
fp = calculateAndTransfer(add,1,2);
cout << "1 + 2 = " << fp(1,2) << endl;
fp = calculateAndTransfer(substract,5,1);
cout << "5 - 1 = " << fp(5,1) << endl;
return 0;
}
输出:
fun(x,y) = 3
1 + 2 = 3
fun(x,y) = 4
5 - 1 = 4
可见,当函数指针出现在输入参数或者返回值位置时,代码可读性将会大大下降,这不利于项目的开发和维护.
通过上面的例子发现,当函数输入参数或者返回值包含函数指针时,代码可读性将会大大降低。为了解决这个问题,C++中可以使用typedef预先给某一类型起别名,然后再用自定义的别名会写出更让人容易理解的代码.
函数/函数指针使用typedef的两种方式:
第一种方式的Fun是自定义的函数别名,第二种方式的PFun是自定义的函数指针别名。在作为形参输入时,二种方式等价(编译器会自动把Fun转换为函数指针),但仍然推荐第二种方式,无需隐式转换,让人更容易理解.
例6、用typedef重写:函数指针作为输入参数:
#include <iostream>
using namespace std;
typedef int Fun(int, int); // 类型别名:Fun代表一类函数
typedef int (*PFun)(int, int); // 类型别名:PFun代表一类函数指针
int add(int x, int y){ // 双参数函数add2:两个数相加
return x + y;
}
int add2(int (*fun)(int, int), int x, int y, int z){ // 利用双参数输入的fun(), 构造三参数函数add2()
return fun(fun(x,y),z);
}
int add4(PFun fun, int x, int y, int z){ // 与add2等价, 利用typedef提高可读性,构造三参数函数add3()
return fun(fun(x,y),z);
}
int add5(Fun fun, int x, int y, int z){ // 与add4等价, 函数名会被自动转换为函数指针作为形参
return fun(fun(x,y),z);
}
int main(){
// 函数指针直接使用:
int (*fp)(int, int);
fp = add;
cout << "1 + 2 = " << fp(1,2) << endl;
// 同上,但使用typedef提高可读性
PFun fp2 = add; // 与fp是同一类型
cout << "1 + 2 = " << fp2(1,2) << endl;
// 函数指针作为输入参数(形参)时,使用typedef的add4和add5的代码可读性比add2更高:
cout << "1 + 2 + 3 = " << add2(fp2, 1, 2, 3) << endl;
cout << "1 + 2 + 3 = " << add4(fp2, 1, 2, 3) << endl;
cout << "1 + 2 + 3 = " << add5(fp2, 1, 2, 3) << endl;
return 0;
}
以下两者为等价写法:都是定义一个名为createAlgorithm(int)的函数,输入参数为int,返回值为函数指针pTemp(其类型为int (*pTemp)(int, int)) 。
例7:用typedef重写:函数指针作为返回值:
#include <iostream>
using namespace std;
// PFun是一个函数指针的类型别名,指向函数的类型为int (*pTemp)(int, int)
typedef int (*PFun)(int, int);
// 定义三种运算:空运算、加法、减法
int none(int , int ){
return 0;
}
int add(int x, int y){
return x + y;
}
int substract(int x, int y){
return x - y;
}
// 定义一个名为createAlgorithm(int)的函数:
// 输入参数为int,表示选择哪一种运算符(1为加法,2为减法)
// 返回值为对应运算符的函数指针pTemp(其类型为int (*pTemp)(int, int))
// 等价于:int (*createAlgorithm(int type))(int, int){ xxx }
PFun createAlgorithm(int type){
switch(type){
case 1:
return add;
break;
case 2:
return substract;
break;
default:
return none;
}
}
int main(){
PFun fp = createAlgorithm(1); // fp接收createAlgorithm(1)返回值,二者都是同类型的函数指针,匹配
cout << "1 + 2 = " << fp(1,2) << endl;
fp = createAlgorithm(2); // fp接收createAlgorithm(2)返回值,二者都是同类型的函数指针,匹配
cout << "5 - 1 = " << fp(5,1) << endl;
return 0;
}
输出:
1 + 2 = 3
5 - 1 = 4
注:类型别名除了typedef之外,还有using和decltype,都可以实现函数指针的别名定义。例如下面三条语句定义的PFun1, PFun2, PFun3等价.
例8:typedef、using和decltype三种等价写法:
#include <iostream>
using namespace std;
int add(int x, int y){
return x + y;
}
// 以下为类型别名的三种等价写法:
typedef int (*PFun1)(int, int); // 类型别名:PFun1代表一类函数指针
using PFun2 = int (*)(int, int); // 类型别名:PFun2与PFun1等价
typedef decltype(add) *PFun3; // 类型别名:PFun3与PFun1等价
int main(){
PFun1 fp1 = add;
PFun2 fp2 = add;
PFun3 fp3 = fp2;
cout << "1 + 2 = " << fp1(1,2) << endl;
cout << "1 + 2 = " << fp2(1,2) << endl;
cout << "1 + 2 = " << fp3(1,2) << endl;
return 0;
}
输出:
1 + 2 = 3
1 + 2 = 3
1 + 2 = 3
例9:用typedef重写例5,让代码具有更好的可读性:
#include <iostream>
using namespace std;
typedef int (*PFun)(int, int);
int add(int x, int y){
return x + y;
}
int substract(int x, int y){
return x - y;
}
// 定义一个名为calculateAndTransfer(int)的函数:在内部执行一次计算,并且把输入的函数指针再返回,传递下去
// 输入参数为函数指针(其类型为int (*pTemp)(int, int))和两个int变量
// 返回值为同样的函数指针pTemp(其类型为int (*pTemp)(int, int))
// 下面等价于:int (*calculateAndTransfer(int (*fun)(int, int), int x, int y))(int, int){
PFun calculateAndTransfer(PFun fun, int x, int y){
cout << "fun(x,y) = " << fun(x,y) << endl;
return add;
}
int main(){
PFun fp;
fp = calculateAndTransfer(add,1,2);
cout << "1 + 2 = " << fp(1,2) << endl;
fp = calculateAndTransfer(substract,5,1);
cout << "5 - 1 = " << fp(5,1) << endl;
return 0;
}
输出:
fun(x,y) = 3
1 + 2 = 3
fun(x,y) = 4
5 - 1 = 6
定义一个由函数指针组成的数组:([]的优先级高于*,因此直接把(*fp)变为(*fp[3])即可 。
int (*fp[3])(int, int)
: fp是一个数组,元素个数为3,每个元素是一个函数指针(指向int (*pf)(int, int)的类型)int (*(fp[3])(int, int)
: 与上面等价,fp是一个数组int ((*fp)[3])(int, int)
: (非法,编译会报错:```error: declaration of 'gp' as array of functions):fp是一个指针,指向一个数组,这个数组的元素个数必须为3,每个元素是一个函数,注意,是函数而不是函数指针,*符号给到fp头上了,因此,编译器会认为你要创建函数的数组,这是不允许的,只允许创建函数指针的数组。例10:函数指针数组:
#include <iostream>
using namespace std;
typedef int (*PFun)(int, int);
int add(int x, int y){
return x + y;
}
int substract(int x, int y){
return x - y;
}
int main(){
// fp是一个数组,元素个数为2,每个元素是一个函数指针(指向int (*pf)(int, int)的类型)
int (*fp[2])(int, int);
fp[0] = add;
fp[1] = substract;
cout << fp[0](5,3) << endl;
cout << fp[1](5,3) << endl;
// 下面会报错,从字面上理解:gp是一个指针,指向函数的数组,而函数的数组是非法的,函数指针的数组是允许的
// int ((*gp)[2])(int, int); \\ error: declaration of 'gp' as array of functions
return 0;
}
输出:
8
2
与普通函数指针类似,类的静态/非静态成员函数(方法)指针也有声明、赋值、调用三个步骤:
普通函数 | 类的非静态成员函数 (类名为A,实例对象为a,对象指针为pa) |
类的静态成员函数(类名为A) | |
---|---|---|---|
函数声明 | int fun(double x, double y); | int A::fun(double x, double y); | int A::static_fun(double, double); |
函数指针的声明 | int (*fp)(double, double); | int (A::*fp2)(double x, double y); | int (*fp3)(double, double); |
函数指针的赋值 | fp = fun; 或 fp = &fun; | fp2 = A::fun;或 fp2 = &A::fun; | fp3 = A::static_fun; |
函数指针的调用 | fp(x, y); 或 (*fp)(x, y); | (a.*fp2)(x,y); (pa->*fp2)(x,y); |
fp3(x, y); 或 (*fp3)(x, y); |
几个注意点:
::*
, 调用.*
和->*
。.*
和->*
的优先级低于()
。总之,类的静态成员函数与普通函数非常类似,只有在赋值的时候需要等号右侧是类名::静态函数的形式,其他的语法完全一致。而类的非静态成员函数在声明时前面要加类名,赋值时等号右侧要加类名,调用时前面要加具体的对象名.
例11:比较普通函数、类的非静态成员函数、类的静态成员函数的声明、赋值和调用:
#include <iostream>
#include <string>
using namespace std;
class Mobile{
public:
Mobile(string number):_number(number){}
void from(string another_number){
cout << _number << " is receiving a call from " << another_number << endl;
}
void to(string another_number){
cout << _number << " is calling to " << another_number << endl;
}
static void printInfo(){
cout << "Static function: Mobile works good!" << endl;
}
private:
string _number;
};
void fun(string number){
cout << "Normal function: number " << number << endl;
}
int main(){
// 普通函数的指针:
void (*fp1)(string); // 声明
fp1 = fun; // 赋值
fp1("999"); // 调用
(*fp1)("999"); // 调用的第二种等价方式
// 类的非静态成员函数(方法)的指针:
Mobile m("666888"), *mp = &m;
void (Mobile::*fp2)(string); // 声明,使用 ::* (小括号不能少:写成Mobile::*fp2(string)会报错)
fp2 = Mobile::from; // 赋值,也可以写成fp2 = &Mobile::from; 因为::优先级高于&
(m.*fp2)("12345"); // 调用,不能写成 m.*fp2("12345") 因为.*的优先级低于()
// (*(m.*fp2))("12345"); // 错误,调用没有像普通函数一样的第二种等价方式(前面加*)
(mp->*fp2)("12345"); // 调用,不能写成 m->*fp2("12345") 因为->*的优先级低于()
fp2 = Mobile::to; // 赋值
(m.*fp2)("54321"); // 调用
(mp->*fp2)("54321"); // 调用
// 类的静态成员函数(方法)的指针:
void (*fp3)(); // 声明: 同普通函数
fp3 = Mobile::printInfo; // 赋值:等号右侧用类名::静态函数,也可以写成fp3 = &Mobile::printInfo;
fp3(); // 调用:同普通函数
(*fp3)(); // 调用的第二种等价方式:同普通函数
return 0;
}
输出:
Normal function: number 999
Normal function: number 999
666888 is receiving a call from 12345
666888 is receiving a call from 12345
666888 is calling to 54321
666888 is calling to 54321
Static function: Mobile works good!
Static function: Mobile works good!
C++ Primer(第5版)Ch 6.7 函数指针 。
C++ Primer Plus (第6版) Ch 7.10 函数指针 。
万字长文系统梳理一下C++函数指针 - 知乎 (zhihu.com) 。
C++ 函数指针 & 类成员函数指针 | 菜鸟教程 (runoob.com) 。
最后此篇关于C++函数指针详解的文章就讲到这里了,如果你想了解更多关于C++函数指针详解的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
C语言sscanf()函数:从字符串中读取指定格式的数据 头文件: ?
最近,我有一个关于工作预评估的问题,即使查询了每个功能的工作原理,我也不知道如何解决。这是一个伪代码。 下面是一个名为foo()的函数,该函数将被传递一个值并返回一个值。如果将以下值传递给foo函数,
CStr 函数 返回表达式,该表达式已被转换为 String 子类型的 Variant。 CStr(expression) expression 参数是任意有效的表达式。 说明 通常,可以
CSng 函数 返回表达式,该表达式已被转换为 Single 子类型的 Variant。 CSng(expression) expression 参数是任意有效的表达式。 说明 通常,可
CreateObject 函数 创建并返回对 Automation 对象的引用。 CreateObject(servername.typename [, location]) 参数 serv
Cos 函数 返回某个角的余弦值。 Cos(number) number 参数可以是任何将某个角表示为弧度的有效数值表达式。 说明 Cos 函数取某个角并返回直角三角形两边的比值。此比值是
CLng 函数 返回表达式,此表达式已被转换为 Long 子类型的 Variant。 CLng(expression) expression 参数是任意有效的表达式。 说明 通常,您可以使
CInt 函数 返回表达式,此表达式已被转换为 Integer 子类型的 Variant。 CInt(expression) expression 参数是任意有效的表达式。 说明 通常,可
Chr 函数 返回与指定的 ANSI 字符代码相对应的字符。 Chr(charcode) charcode 参数是可以标识字符的数字。 说明 从 0 到 31 的数字表示标准的不可打印的
CDbl 函数 返回表达式,此表达式已被转换为 Double 子类型的 Variant。 CDbl(expression) expression 参数是任意有效的表达式。 说明 通常,您可
CDate 函数 返回表达式,此表达式已被转换为 Date 子类型的 Variant。 CDate(date) date 参数是任意有效的日期表达式。 说明 IsDate 函数用于判断 d
CCur 函数 返回表达式,此表达式已被转换为 Currency 子类型的 Variant。 CCur(expression) expression 参数是任意有效的表达式。 说明 通常,
CByte 函数 返回表达式,此表达式已被转换为 Byte 子类型的 Variant。 CByte(expression) expression 参数是任意有效的表达式。 说明 通常,可以
CBool 函数 返回表达式,此表达式已转换为 Boolean 子类型的 Variant。 CBool(expression) expression 是任意有效的表达式。 说明 如果 ex
Atn 函数 返回数值的反正切值。 Atn(number) number 参数可以是任意有效的数值表达式。 说明 Atn 函数计算直角三角形两个边的比值 (number) 并返回对应角的弧
Asc 函数 返回与字符串的第一个字母对应的 ANSI 字符代码。 Asc(string) string 参数是任意有效的字符串表达式。如果 string 参数未包含字符,则将发生运行时错误。
Array 函数 返回包含数组的 Variant。 Array(arglist) arglist 参数是赋给包含在 Variant 中的数组元素的值的列表(用逗号分隔)。如果没有指定此参数,则
Abs 函数 返回数字的绝对值。 Abs(number) number 参数可以是任意有效的数值表达式。如果 number 包含 Null,则返回 Null;如果是未初始化变量,则返回 0。
FormatPercent 函数 返回表达式,此表达式已被格式化为尾随有 % 符号的百分比(乘以 100 )。 FormatPercent(expression[,NumDigitsAfterD
FormatNumber 函数 返回表达式,此表达式已被格式化为数值。 FormatNumber( expression [,NumDigitsAfterDecimal [,Inc
我是一名优秀的程序员,十分优秀!