前面整理了 C++指针的基础知识和指针/数组作为函数参数的用法
链接在此↓↓↓↓↓↓,欢迎点击
既然说到了指针和函数,那这里就必须在说一下函数指针。
也就是 指向一个函数 的指针。举个简单的例子
一、函数指针的定义与调用
简单的定义三个函数,分别 实现“加”、“减”、“乘”三种运算
int add(int num1, int num2)
{
return num1 + num2;
}
int minu(int num1, int num2)
{
return num1 - num2;
}
int times(int num1, int num2)
{
return num1 * num2;
}
这三个函数的共性是输入两个整型数据,而后通过运算 返回一个整型数据,这样定一个函数指针:
int (*pf)(int, int) = NULL;
//int表示函数返回值为int,
//pf是函数指针变量
//(int, int)是指函数有两个int型参数
这样,就定义了一个 具有两个int型参数,返回一个int型结果的函数指针。下面就需要让这个 指针指向特定函数,并进行调用
请看代码:
int main()
{
int n1 = 12;
int n2 = 11;
pf = &add;
cout << "add n1 n2=\t" << pf(n1, n2) << endl;
pf = minu;
cout << "minus n1 n2=\t" << (*pf)(n1, n2) << endl;
pf = times;
cout << "times n1 n2=\t" << pf(n1, n2) << endl;
cin.get();
return 0;
}
运行结果如下 :
add n1 n2= 23
minus n1 n2= 1
times n1 n2= 132
由此总结,函数指针赋值和调用的方式可以有两种
pf = &add; //赋值
pf = minu; //赋值
//-----------------
pf(n1, n2); //调用
(*pf)(n1, n2); //调用
即函数名前面加“&”或者不加都可以进行赋值,在调用的时候可以带上“*”号,或者不带都是可以的。
以上即是函数 指针的定义与调用方式,也可以看出,使用函数指针,同一个指针可以指向同类型的不同函数,在样可以在编程时根据实际需要,让函数 指针指向不同函数,大大提高代码的复用率以及灵活性。
下面还是借助上面的三个简单函数,写如下函数
二、函数参数作为返回值
int (*fuc1(int index))(int, int)
{
switch (index)
{
case 0:
return add;
break;
case 1:
return minu;
break;
case 2:
return times;
break;
default:
return add;
break;
}
}
该函数根据输入参数的值,返回不同的函数, 例如输入0, 返回add(num1, num2)的函数。该如何理解这样的写法呢:
函数名称是func1,它具有一个参数(int index);
这个函数 的返回值是 int (*)(int, int),是一个具有两个int型参数以及 返回值是int型的函数指针。
也就是说,当函数返回值为 函数指针时,其写法按如下格式:
返回的函数的返回类型 (*函数名(参数))(返回的函数的参数各类型)
对 以上函数进行调用,如下:
int main()
{
cout << "fuc1(1)=\t" << fuc1(1)(5,4) << endl;
cin.get();
return 0;
}
调用时,因为函数func返回值是函数 指针,所以func(1),就相当于一个函数指针,指向minu()函数,
func(1)(5,4)就相当于minu(5,4)。
三、函数指针作为参数
由上面已知函数指针可以作为函数返回值,那必然也可以作为函数的参数。即:我们将函数指针作为另一个函数的参数进行传递
例如下面:
int clcnums(int a[3], int(*pf)(int,int))
{
return pf(a[0], pf(a[1], a[2]));
}
函数clcnums的两个参数,一个是 具有三个元素的数组,第二个是一个函数指针,即可以传入一个函数
例如这样调用该函数:
int main()
{
int num[3];
num[0] = 5; num[1] = 4; num[2] = 3;
cout << "clcnums=\t" << clcnums(num,times) << endl;
cin.get();
return 0;
}
我们将前面定义过的相乘 运算的函数传入,这样就能 计算出num[3]中,三个元素相乘的结果了
四、函数指针数组
看这名字似乎是一个很酸爽的东西,又是函数,又是指针,又是数组的,但是说简单点:
一个指针,指向一个数组,数组里的元素都是指针,这些元素指针指向的都是函数。这样说来,就是一件很简单的事了。
在语法上如何写呢,如下:
int main()
{
//定义函数指针数组pfunc[3],里面是三个函数
int(*pfunc[3])(int, int) = { add,minu,times };
//循环调用,依次输出结果
for (int i = 0; i<3; i++)
{
cout << "pfunc(9,6)=" << pfunc[i](9, 6) << endl;
}
cin.get();
return 0;
}
同样的,函数指针数组也可以作为函数参数:
void pfuncs(int a[3], int(*pf[3])(int, int))
{
for (int i = 0; i < 3; i++)
{
cout << "pfuns " << i << "="<< pf[i](a[0], pf[i](a[1], a[2])) << endl;
}
}
int main()
{
//定义函数指针数组pfunc[3],里面是三个函数
int(*pfunc[3])(int, int) = { add,minu,times };
int num[3];
num[0] = 5; num[1] = 4; num[2] = 3;
//出入函数指针数组
pfuncs(num, pfunc);
cin.get();
return 0;
}
/*--------------------------------
pfuns 0=12
pfuns 1=4
pfuns 2=60
--------------------------------*/
……
五、指向类成员函数的指针
如下是几种常见的类:非静态类中的非静态函数、非静态类类中的静态函数、静态类中的非静态函数、静态类中的静态函数:
//----------1
class pfclass
{
public:
void fun1(int a,int b)
{
cout << "a+b=\t" << a + b << endl;
}
};
//----------2
class pfclass2
{
public:
static void sfun1(int a, int b)
{
cout << "a+b=\t" << a + b << endl;
}
};
//----------3
static class spfclass
{
public:
void sfun1(int a, int b)
{
cout << "a+b=\t" << a + b << endl;
}
};
//----------4
static class spfclass2
{
public:
static void sfun1(int a, int b)
{
cout << "a+b=\t" << a + b << endl;
}
}sc;//静态类唯一实例对象
如何 定义函数指针 指向这些类的成员函数呢, 方法如下:
先看第一个是普通类的非静态成员函数指针
int main()
{
pfclass pfc;
void (pfclass::*pfun1)(int, int) = NULL;
pfun1 = &pfclass::fun1;
//不加"&":“pfclass::fun1”: 非标准语法;请使用 "&" 来创建指向成员的指针
(pfc.*pfun1)(5, 6);
cin.get();
return 0;
}
声明指针属于pfclass类,再对该指针进行赋值,赋值时还需声明所赋予的函数所属类,并且前面必须加“&”,否则会报语法错误。调用时由类的对象实例进行调用
所以:当函数指针指向其他非静态类的非静态成员函数时,在声明函数指针时必须用双冒号来限定指针所属类;在赋值时,“&” 并不是可有可无的,而是必须有的,且要在函数名前用 双冒号注明所属类。
第二个是指向非静态类的静态成员函数
int main()
{
void(*pf1)(int, int) = NULL;
//void(pfclass2::*pf1)(int, int) = NULL;
//不能将 "void (*)(int a, int b)" 类型的值分配到 "void (pfclass2::*)(int, int)" 类型的实体
pf1 = &pfclass2::sfun1;
pf1(5, 6);
cin.get();
return 0;
}
如果声明函数指正时,想前一种一样(代码段中的注释内容),在进行 赋值时,就会报错,不能将 "void (*)(int a, int b)" 类型的值分配到 "void (pfclass2::*)(int, int)" 类型的实体,也就是说该静态成员 函数实际上是void (*)(int a, int b)类型,也就是前文一直在用的类型:
函数指针指向 非静态类的静态成员函数时,声明函数指针时不用指明函数指针所属类,但在赋值时,所赋予的静态函数要说明所属类,调用时也不需要类的实例化对象。
下面看第三种:静态类中的 非静态成员函数
int main()
{
void(spfclass::*spf1)(int, int) = NULL;
spf1 = &spfclass::sfun1;
spfclass().sfun1(5, 6);
(spfclass().*spf1)(5, 6);
cin.get();
return 0;
}
静态类是不能被实例化的,但其成员函数又是非静态的,所以在声明函数指针和赋值时,和非静态类的非静态成员函数一致,只是在调用时,只能由函数名加括号加点直接调用。
这里有一点需要注意,就是像以上这样写时,会有一个警告:
“static ”: 没有声明变量时忽略“spfclass”的左侧
该警告指向的位置是该静态类的末尾,如何理解呢?是这样的,虽然静态类是不希望被实例化而存在的,但在C++ 中的静态类, 规范的写法是需要实例化的,但 只能被实例化一次,就像第四个类,静态类、且成员函数也是静态的
在该静态类的结尾,“}” 之后,需要实例化这个静态类,而这个实例化对象,是这个静态类的唯一对象,在调用该类的成员函数时,可以使用函数名加 括号加点的形式,也可以使用这个唯一示例对象加点的方式调用。
例如对第四个类,静态类spfclass2的末尾,进行实例化 sc,即是这个类的唯一实例化对象,如下主函数中,sc.func1(5,6) 即是对静态类中静态成员函数的调用方式。
第四种:指向静态类中的静态成员函数呢,
函数指针指向静态类中的静态成员函数,其方法与指向非静态类中的静态函数一样,按照函数类型直接声明函数指针,在复制是,所赋予的函数要注明所属类,可以用函数名加双冒号,也可以用静态类的唯一实例对象。在调用时不需要函数名或实例对象
int main()
{
void(*spf21)(int, int) = NULL;
spf21 = &spfclass2::sfun1;
//spf21 = &sc.sfun1;这样也是可以的
sc.sfun1(5, 6);
spf21(5, 6);
cin.get();
return 0;
}
六、声明函数指针类型
前面用到的函数指针,在声明时写法非常复杂,尤其是作为函数参数和返回值时。那么有没有较为机智的方式呢,
必然是有的,这里就要引入“typedef”了。
例如文章开头所写的那三个函数,都是两个int型参数,返回值也是int型数据的函数,这样,用如下代码,来定义一种函数指针,这种函数指针就是 指向这类函数的:
typedef int(*PF)(int, int);
即:PF就是指向一类函数的指针类型,既然是类型,就可以实例化:
PF my_pf = times;
这样的写法就比前面的写法 简单明了多了。
例如函数指针作为函数返回值:
PF func1(int, int)
{
switch (index)
{
case 0:
return add;
break;
case 1:
return minu;
break;
case 2:
return times;
break;
default:
return add;
break;
}
}
再例如做函数参数:
int clcnums(int a[3], PF pf)
{
return pf(a[0], pf(a[1], a[2]));
}
再例如函数指针数组
int main()
{
PF2 pfunc[3] = { add,minu,times };
for (int i = 0; i < 3; i++)
{
cout << "pfunc(9,6)=" << pfunc[i](9, 6) << endl;
}
cin.get();
return 0;
}
函数指针数组做函数参数
void pfuncs(int a[3], PF pf[3])
{
for (int i = 0; i < 3; i++)
{
cout << "pfuns " << i << "="<< pf[i](a[0], pf[i](a[1], a[2])) << endl;
}
}
即:使用
typedef 所指向函数返回值类型(*指向该类函数的指针类型名)(所指向函数的参数各类型)
这种形式,可以定义指向一类函数(参数个数及类型、返回值类型一致)的指针类型,由该类型可实例化多个该类型的函数指针,以方便的代码的编写和阅读。
七、获取函数类型
虽然上面的已经很 简洁了,但还是不够简洁,因为 在 声明时, 我还是要吧函数的参数类型写的十分清楚,但有时, 函数参数过多,就 难免很容易写错,有没有 更加偷懒的方式呢
有,那就是:
“decltype”
其作用就是获取某个函数的函数类型。
例如:
typedef decltype(add)* PF2;
就是定义一类函数指针PF2,这一类函数指针 指向的,都是add函数这种类型。add函数就是两个int型参数,返回值也是int型数据的函数,那么这里的PF2,和上文中的PF就是 相同的。一样可以用它类声明各种函数 指针,例如:
int main()
{
PF2 pfunc[3] = { add,minu,times };
for (int i = 0; i < 3; i++)
{
cout << "pfunc(9,6)=" << pfunc[i](9, 6) << endl;
}
cin.get();
return 0;
}