C++高阶之函数指针

函数指针的定义、赋值与使用

概念

跟指向数据的普通指针一样,函数指针本质上也是一个变量,它记录的也是表示某个内存地址的整数数值。只不过这个内存地址上保存的不是普通数据而是某个函数的代码,所以这个指针就成了指向这个函数的函数指针。

如果某个函数指针的值是某个函数的入口地址,则说这个函数指针指向这个函数。进而,我们可以通过这个函数指针调用它所指向的函数,就如同我们通过指向普通变量的指针访问它所指向的变量一样。

定义

定义一个函数指针的语法形式如下:

函数返回值类型标志符 (*指针变量名)(形式参数列表);

函数返回值类型标志符就是这个指针所要指向的函数的返回值类型。指针变量名就是这个函数指针的名字,由于“( )”的优先级高于*符号,所以指针变量名外的括号必不可少。形式参数列表跟指针所指函数的形式参数列表相同。概括起来,函数指针的定义跟它所指函数的声明相似(返回值类型和形式参数列表相同),只不过是将函数名换成了“*”加上一个指针变量名。而这也隐含了一个信息,一个函数的函数名, 实际上就是指向这个函数的指针

// 一个普通的函数
void PrintPass( int nScore )
{
cout<<nScore<<endl;
}
// 定义函数指针
void (*pPrintFunc)( int nScore );

当定义函数指针时,参数列表中的形式参数名可有可无,以上代码也可以写成下面这种简化的形式:

// 省略形式参数名的函数指针定义
void (*pPrintFunc)( int );

当函数的形式参数比较多时,通常省略形式参数名,让函数指针的定义更加简洁。

函数指针类型-typedef

如果要定义多个同一类型的函数指针,还可以使用 typedef 关键字将这种函数指针类型定义成一种新的数据类型,用这种新的数据类型来定义函数指针

// 定义一种新的函数指针的数据类型
// 这种类型的函数指针可以指向的函数的返回值类型是 void,
// 同时拥有一个 int 类型的参数
typedef void (* PRINTFUNC )(int);
// 使用新的数据类型定义多个同类型的函数指针
PRINTFUNC pFuncFailed;
PRINTFUNC pFuncPass;

这里,就定义了一种新的函数指针类型 PRINTFUNC,它表示这种类型的函数指针可以指向一个返回值类型为 void 同时拥有一个 int 类型参数的函数。

赋值

完成函数指针的定义后,就可以用函数名给函数指针赋值,让它指向这个函数。

// 用函数名给函数指针赋值
pPrintFunc = PrintPass;

使用auto关键字定义并赋值

使用 auto 关键字作为函数指针的数据类型来定义一个函数指针并同时给它赋值,至于这种类型的具体定义就留给编译器根据这个指针的初始值自己去推断好了。

/ 利用 auto 关键字定义函数指针
// 编译器会在变量赋值的时候,自动推断函数指针的具体说明
auto pPrintFunc = PrintPass;

调用

/ 通过函数名直接调用 PrintPass()函数
PrintPass( 75 );
// 通过函数指针 pPrintFunc
// 调用它所指向的 PrintPass()函数
(*pPrintFunc)( 75 );

用函数指针实现回调函数

普通数据指针的意义在于传递数据。一个程序中除了数据之外还有对数据的操作,也同样需要传递。而函数指针的意义就在于传递对数据的操作,也就是传递函数

以函数指针作为函数的参数,我们可以将指向某个函数的指针传递给另一个函数,而在这个函数中,我们可以通过传递进来的函数指针调用它所指向的函数,而这个函数也被称为回调函数( callback function)。根据传递进来的函数指针所指函数的不同,这个函数就可以表现出不同的行为,从而实现了在函数间传递操作,达到了通过参数对函数行为进行自定义的目的

count_if()算法就是通过提供一个函数指针类型的参数,让外界可以向它传递表示统计规则的函数,从而让 count_if ()算法可以完成各种条件的统计。

在这里,我们也利用函数指针实现一个简化版本的 mycount_if()算法,让它通过函数指针参数也可以接受多种统计规则函数,从而统计一个 vector 容器中多种符合条件的数据的个数:

// 定义函数指针类型 RuleFunc,它可以指向返回值为 bool 类型,
// 同时拥有一个 int 类型参数的函数
typedef bool (*RuleFunc)(int);
// 定义算法框架函数
int mycount_if(const vector<int>& v, // 需要统计的容器
RuleFunc is) // 指向统计规则函数的函数指针
{
	int nTotal = 0;
	// 使用序列 for 循环遍历容器中的数据
	for(int n:v)
	{
		// 通过函数指针调用规则函数,
		// 判断当前数据是否符合统计规则
		if(is(n))
		{
			++nTotal; // 如果符合,则统计在内
		}
	}
	return nTotal; // 返回统计结果
}
// 统计规则函数,判断分数是否及格
bool IsPass(int n)
{
	return n >= 60;
}
// 统计规则函数,判断分数是否不及格
bool IsFailed(int n)
{
	return n < 60;
}
// 利用 mycount_if()算法统计 vector 容器中的数据
int main()
{
	// 待统计的容器,添加初始数据
	vector<int> vecScore = {54,87,65,31,94};
	// 使用 IsPass 函数名作为 mycount_if()算法的参数,
	// 统计容器中的及格分数的个数
	int nPass = mycount_if(vecScore,IsPass);
	// 更换统计规则函数,统计容器中的不及格分数的个数
	int nFailed = mycount_if(vecScore,IsFailed);
	// 输出结果
	cout<<"及格人数"<<nPass<<endl;
	cout<<"不及格人数"<<nFailed<<endl;
	return 0;
}

将函数指针应用到 STL 算法中

为 STL 中的 count_if()算法提供一个指向统计规则函数的函数指针,让它统计容器中所有符合条件的数据的个数:

// 利用函数定义统计的规则,统计身高大于 170 的 Student 对象
bool countHeight( const Student& st )
{
	// 如果身高大于 170,则统计在内
	return st.GetHeight() > 170;
}
// 将统计规则函数的指针 countHeight 应用到 count_if()算法中
// 这样 count_if()算法将调用 countHeight()函数来判断数据是否符合条件
int nCount = count_if(vecStu.begin(), vecStu.end(), countHeight );
cout<<"身高大于 170 的学生有: "<<nCount<<endl;

为了让这个统计算法具有更大的灵活性,我们可以对统计规则函数 countHeight()进行改写,将统计标准也作为函数的参数,在调用时再根据具体情况给定统计标准,让这个统计算法更加灵活:

// 将统计标准也作为参数,重新定义统计规则函数
bool countHeight( const int nHeight, const Student st )
{
// 如果身高大于标准身高,则统计在内
	return st.GetHeight() > nHeight;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值