C++中,函数的名字本身就是该函数的指针,函数名代表函数的入口地址, 是可以作为参数传入函数的。
一、函数指针的定义
函数类型 (*自定义变量名)(形参1,形参2,...) ;
int (*p)(int, int); // 定义一个指向函数的指针-->p,被指向的函数有两个整型参数并返回整型值。
上式可以看作这样:
int (*)(int, int) p;
其中,int (*)(int, int) 是变量类型, p是变量名。
例1:
#include<iostream>
using namespace std;
int main()
{
int add(int x, int y);
int a = 2, b = 3, sum;
// 定义一个指向函数的指针-->p,被指向的函数有两个整型参数并返回整型值。
int(*p)(int x, int y);
p = add; // p指向一个函数的起始地址,它相当于一个函数的"别名"。可以写成p = &add;
sum = add(a, b);
cout << "sum: " << sum << endl;
sum = p(a, b);
cout << "sum: " << sum << endl;
sum = (*p)(a, b); // (*p)() == (*&add)() == add()
cout << "sum: " << sum << endl;
return 0;
}
int add(int x, int y) {
return (x + y);
}
函数输出:
sum: 5
sum: 5
sum: 5
函数名代表函数的入口地址。
函数指针,指向一个函数的起始地址,它相当于一个函数的别名,它的返回值是int型(不是指针)
p = add; // 有一层默认的转换,实际上是 p = &add;
(*p)() == (*&add)() == add()
注意:下面这三种写法是等价的。
int(*p)(int x, int y); p = add;
int(*p)(int x, int y) = add;
int(*p)(int x, int y) = &add;
因为形参名可以不写,在代码中,也可以写成下面这样,也能得到同样的输出:
int(*p)(int, int); p = add;
int(*p)(int, int) = add;
int(*p)(int, int) = &add;
可以看作
int(*)(int, int) p = &add; //形如 int a = 3;
二、函数作为形参的语法格式
例2:
#include<iostream>
using namespace std;
// 其中: void (*)(int) 为形参的类型,func 为形参变量。
void foo(void (*func)(int),int j)
{
func(j);
}
void printnumber(int j)
{
cout<<"j="<<j<<" "<<endl;
}
int main()
{
void (*func)(int i); // 声明一个函数的指针变量--func,该函数有一个int型的参数,无返回值。
func = printnumber; // 将printnumber的地址赋给func。
foo(func,1);//(1)
foo(printnumber,1);//(2)
// (1)、(2)两种写法是等价的。
}
个人觉得,可以将
void foo( void (*func)(int), int j)
看作
void foo( void (*)(int) func, int j)
三、typedef定义新的函数类型
例3:用typedef定义一类函数,Linux中最常用。
#include<iostream>
using namespace std;
typedef void(*func)(int, int);
//这样func就可以代表一类函数;
//使用这个定义
void runfunc(func p, int a, int b)
{
p(a, b);
}
void add(int a, int b)
{
cout << a + b << endl;
}
void sub(int a, int b)
{
cout << a - b << endl;
}
int main() {
//函数作参数
runfunc(add, 1, 2);
runfunc(sub, 1, 2);
return 0;
}
个人觉得,可以将
typedef void(*func)(int, int);
看作
typedef void(*)(int, int) func;
这样 func 就可以代表一类函数。
四、类成员函数指针的使用方法
例4:
#include<iostream>
using namespace std;
class A {
public:
void fun(int a)
{
cout << a << endl;
}
};
int main()
{
A a;
void (A::*ptrfun)(int);
ptrfun = &A::fun;
(a.*ptrfun)(2);
return 0;
}
如果 void fun(int a)
是普通函数,对普通函数指针进行赋值时,直接写成pfun = fun
。 但是,如果 void fun(int a)
是类的成员函数,对类的成员函数指针进行赋值时,必须写成pfun = &A::fun
。
这涉及到函数类型与函数指针类型。对于普通函数,当定义了一个函数指针,将函数名赋值给函数指针,函数类型会默认转换成函数指针。因此 pfun = fun
或者 pfun = &fun
都是可行的,只是 pfun = fun
中有一层默认的转换。
而对于类的成员函数,当定义了一个成员函数指针 ptrfun
,如果直接用 A::fun
进行赋值,编译器会将它理解为A中的静态成员,会产生编译错误,所以,这里的&是必须的。
不过,如果写成这样 ptrfun = &(A::fun)
;也会发生编译错误,因为,将 A::fun
作为一个整体,编译器就会将fun理解为A中的静态成员,所以,要将 &A::fun
作为一个整体,即使::的优先级较高。同样,对于调用方式也有类似的不同。对于普通函数,(*pfun)()和pfun()都是可以的。对于成员函数,只能是 (a.*ptrfun)()
。
五、使用类成员函数的指针作为参数
例5:
#include<iostream>
using namespace std;
class A {
public:
void fun(int a)
{
cout << a << endl;
}
};
void test(A x, void (A::*pfun)(int), int y)
{
(x.*pfun)(y);
}
int main()
{
void (A::*ptrfun)(int);
ptrfun = &A::fun;
A a;
test(a, ptrfun, 3);
return 0;
}
例6:类成员函数和函数重载版本
函数重载:同一个作用域下,函数名称相同。重载函数的参数个数、参数类型或者参数顺序,三者中必须至少有一个不同。
注意: 函数的返回值不可以作为函数重载的条件。
#include<iostream>
using namespace std;
class A {
public:
void fun1(int a)
{
cout << a << endl;
}
int fun2(int a)
{
cout << "this的值: " << this << endl;
cout << a << endl;
return a;
}
};
void test(A x, void (A::*pfun)(int), int y)
{
(x.*pfun)(y);
}
// test函数的重载版本,x是引用
void test(A &x, int (A::*pfun)(int), int y)
{
(x.*pfun)(y);
}
int main()
{
void (A::*ptrfun)(int);
ptrfun = &A::fun1;
A a;
test(a, ptrfun, 3);
cout << "------分割线------"<< endl;
test(a, &A::fun2, 4);
cout << "&a的值: " << &a << endl;
return 0;
}
代码输出:
3
------分割线------
this的值: 003BFA73
4
&a的值: 003BFA73
不论类的成员函数,在类内定义还是在类外定义,成员函数的代码段的存储方式是相同的,都不占用对象的存储空间。 当程序运行时,才会给类的成员函数分配地址。 当对象 a 调用类的成员函数 fun2() 时,this 指针就指向 a,类的成员函数就会访问 a 的数据成员。
可以看出,通过对 test 函数进行重载,可以实现:调用不同类型的类成员函数的效果。对象 a,只是给类的成员函数提供 this 指针。这样可以确保类的成员函数 fun2() 访问的是 a 的成员变量。
补充知识:this指针
每一个成员函数都包含一个特殊的指针,这个指针指针的名字是固定的,称为this。它是指向本类对象的指针,它的值是当前被调用对象的起始地址。 this指针是隐式调用的,它是作为参数被传递给成员函数的。假如,原本的成员函数volume的定义是:
int Box::volume() {
return h * w * l;
}
C++把它处理为
int Box::volume(Box *this) {
return this->h * this->w * this->l;
}
在调用该成员函数时,实际上是用已下的方式调用的:
a.volume(&a);
将对象a的地址传给形参this指针。然后按this的指向去引用其他成员。
参考链接:
函数作为参数
函数参数与函数作为参数
将成员函数作为函数形参
C++typedef的详细用法
c++将函数作为函数参数(函数指针)