「C++学习笔记」指针篇3:函数指针

前面整理了 C++指针的基础知识和指针/数组作为函数参数的用法

链接在此↓↓↓↓↓↓,欢迎点击

「C++学习笔记」指针的理解

「C++学习笔记」指针篇2:指针及数组作为函数参数

既然说到了指针和函数,那这里就必须在说一下函数指针。

也就是 指向一个函数 的指针。举个简单的例子

一、函数指针的定义与调用

简单的定义三个函数,分别 实现“加”、“减”、“乘”三种运算

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;
}

发布了21 篇原创文章 · 获赞 5 · 访问量 3676

猜你喜欢

转载自blog.csdn.net/Raink_LH/article/details/89066068