先看看typedef的用法
tepdef是系统保留字,可以为指针定义简介的名称,如
typedef int (*MyFUN)(int a,intb);
int Max(int a,int b);
MyFUN pMyFun;
pMyFun= Max;
其中MyFUN代表指向函数的指针类型的新名称,为指向函数的指针的类型别名。
- 再说一下复杂声明的分析
理解复杂声明可用的“右左法则”:从变量名看起,先往右,再往左,碰到一个圆括号就调转阅读的方向;括号内分析完就跳出括号,还是按先右后左的顺序,如此循环,直到整个声明分析完
举个例子
int (*func[5])(int *);
func右边是一个[ ]运算符,说明func是具有5个元素的数组;func的左边有一个*,说明func[ ] 的元素是指针(注意这里的不是修饰 func,而是修饰func[5]的,原因是[ ]运算符优先级比高,func先跟[ ]结合)。跳出这个括号,看右边,又遇到圆括号,说明func数组的元素是函数类型的指针,它指向的函数具有int*类型的形参,返回值类型为int。
- C++回调
在C++中,服务器编程常使用线程池+任务回调,需要用函数指针。
#include <cstdio>
//第一步,声明函数指针。下面是声明一个函数指针,这个函数指针的类型名叫func,它指向的函数返回值为void,调用的参数为int
typedef void(*func)(int);
void Print(int i ){
printf("%d\n", i);
}
int main () {
// 第二步,定义一个函数指针,并且指向一个符合函数指针声明的函数
func f = &Print;
f(1); //调用函数
return 0;
}
如果想加点面向对象的思想,我们可以多定义一个结构体,然后把回调的函数指针和调用的参数给封装起来
typedef void(*func)(int);
struct MyStruct{
int param;
func callback;
}:
在看一个服务器项目中的例子:
我的主线程中的一个epoll事件是如果服务端关闭连接,删除对应的套接字数据。
先看看自定义的用户数据结构client_data
class util_timer;
struct client_data
{
sockaddr_in address;
int sockfd;
//用户数据类里面的成员对象包含了回调函数的指针,便于之后使用
util_timer *timer;
};
class util_timer
{
public:
util_timer() : prev(NULL), next(NULL) {}
public:
time_t expire;
void (* cb_func)(client_data *);
client_data *user_data;
util_timer *prev;
util_timer *next;
};
主线程中定义的回调函数,以及逻辑
void cb_func(client_data *user_data)
{
epoll_ctl(epollfd, EPOLL_CTL_DEL, user_data->sockfd, 0);
assert(user_data);
close(user_data->sockfd);
http_conn::m_user_count--;
LOG_INFO("close fd %d", user_data->sockfd);
Log::get_instance()->flush();
}
client_data *users_timer = new client_data[MAX_FD];
//...
//为每个连接注册回调函数
for() {
users[connfd].init(connfd, client_address);
util_timer *timer = new util_timer;
timer->user_data = &users_timer[connfd];
}
//...
else if (events[i].events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR))
{
//服务器端关闭连接,移除对应的定时器
util_timer *timer = users_timer[sockfd].timer;
timer->cb_func(&users_timer[sockfd]);//回调函数的调用
if (timer)
{
timer_lst.del_timer(timer);
}
}
实际中大概就是这样的过程,之前一直没有太理解:为什么必须用指针的形式,对于特定的业务逻辑我直接调用函数不行么?理论上其实也可以,但是这样在复杂的项目也会比较臃肿,用指针的形式有点类似与函数形式的多态,相当于我只要写好形参即可,至于被调用函数的实际定义可能暂时不确定,或者不是我写/有多种形式等等,指针形式回调函数可以解决这个问题。通俗来说提供了一个接口。