C++中的函数类型(1):函数指针

在C++中如何传递一个函数,比如作为类或者其他函数的参数。
需要有表示函数的类型,有几个种不同的方式。
本来想把所有函数类型总结一下,函数指针整理完了,零零碎碎的就不少,分几篇吧。

函数指针

从c语言继承来的方式,最基本也是最本质的表示函数类型的方式。
函数就是一些连续的指令,程序加载到内存中后,本质上一个内存地址表示函数入口。
程序运行时调用函数,准备好参数堆栈、寄存器后,跳转到函数入口地址,开始执行相应的逻辑。
执行完成后,清理堆栈、保存数据,返回调用方。

相较其他方式,函数指针类型定义不是很直观。优点是,不依赖于已有函数(参照下面集中定义方式),最直接的定义方式。
对人来说可读性稍差,对编译器比较直接。

示例代码

上示例代码,看下面的说明:

// 一个例子函数
int func(int a) {
	return a+1;
}

typedef int (*func_type) (int); // typedef定义函数指针类型
using func_type_other = int (*) (int);  // using定义指针类型语法

func_type ptr2 = func; // 给函数指针赋值,指向具体函数,函数名本身就是指针
func_type ptr3 = &func; // 给函数指针赋值,指向具体函数,对函数名去地址操作,效果等同直接用函数名

cout << ptr2(1); // 输出2
cout << ptr3(2); // 输出3

// 函数指针作为参数使用
void call_func(func_type one_func) {
	cout << "call_func: " << one_func(5) << endl; // 输出 6
}

call_func(func); // 直接传入函数名
call_func(ptr2); // 传入函数指针

定义函数指针基础步骤(定义方式1)

1)首先写一个函数原型的声明,上面的例子就是:
int func(int a);
2)把函数名用括号括起来,然后把函数名替换成“*类型名”,其他不变。
输入参数,只提供类型,参数名可有可无。
int (*func_type) (int a);
3)前面加上typedef:
typedef int (*func_type) (int a);

大功告成,func_type就是要定义的函数指针类型,可以定义函数指针的变量或参数。

说明

a)typedef声明中,类型前面的星号*是必需的,只有指针类型才能和函数名代表的地址类型匹配。
b)函数名(func)和对函数名取地址操作(&func),等效。
c)定义步骤中,1)和2)按先后顺序,3)步放在哪里都可以。

用decltype声明函数指针(定义方式2)

通过使用C++新增的decltype可以简化函数指针定义,前提是有一个同类型的函数。
如果是开发函数库时,在没有函数的情况下,定义一个接口类型,就只能用上面的方式去定义了。

// 例子函数
int func(int a) {
	return a+1;
}

typedef decltype(func) *func_type_two;
using func_type_thress = decltype(func)*; // 注意末尾星号*,func_type_thress和func_type_two完全等价,只是新旧语法差异

func_type_two ptr4 = func;
// func_type_thress ptr4 = func;
cout << ptr4(6);

// 函数指针作为参数使用
void call_func(func_type one_func) {
	cout << "call_func: " << one_func(5) << endl; // 输出 6
}
call_func(ptr4); // 可以调用,说明不同方式定义的函数指针类型相同

说明

a)类型前面的星号*是必需的,使用方式和前面的一样。
b)使用把ptr4传递给call_func,检验不同方式定义的函数指针类型相同。
其实,只要是能把函数名赋值给函数指针,就说明是相同的类型。

用终极必杀auto声明函数指针(定义方式3)

搞不定了就用auto兜底。同样,需要现有一个参考函数。对于直接定义参数类型的不合适。
适用于在代码处理过程中,存储或者传递一个函数指针。
写起来简单,对于不熟悉代码逻辑的人,可读性并不好。

// 例子函数
int func(int a) {
	return a+1;
}

auto *ptr5 = func;
auto ptr6 = func;
auto ptr7 = &func; // ptr5, ptr6, ptr7都是同样类型的函数指针

auto &ptr8 = func; // ptr8是函数的引用,编译器内部处理函数引用和指针一样,通过下面的调用可以验证
ptr8(8);
call_func(ptr8);

函数引用,效果等同函数指针(定义方式4)

// 例子函数
int func(int a) {
	return a+1;
}

auto &ptr8 = func; // ptr8是函数的引用,编译器内部处理函数引用和指针一样,通过下面的调用可以验证
ptr8(8);
call_func(ptr8);

using定义模板类型指针

这中能力用typedef无法实现,但又是模板库中是基本能力。应该优先使用using语法。

template <typename T>
using Compare = bool (*) (T&, T&);

template <typename T>
void sort(vector<T> vec, Compare<T> compare);

总结

后续新增的各种“语法糖”简化了定义方式,本质上行最基础的定义方式是一致的。
关键要明白指针指向函数入口地址的本质,其他的只是辅助编译器识别函数指针的类型,包括输入、输出类型。

猜你喜欢

转载自blog.csdn.net/yinminsumeng/article/details/129232645
今日推荐