关于函数指针和回调函数的用法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_39568531/article/details/80008228
#ifndef CALLBACK_FUNC_H
#define CALLBACK_FUNC_H

#include <QMainWindow>

typedef void (*FUNC)(int);

class callback_func : public QMainWindow
{
    Q_OBJECT

public:
    callback_func(QWidget *parent = 0);
    ~callback_func();
public:
    FUNC print(int a);

    static void hello(int);
    static void bye(int);
    static void ok(int);
};

#endif // CALLBACK_FUNC_H

#include "callback_func.h"
#include"stdio.h"

#include"QDebug"

#define USE_SWITCH
callback_func::callback_func(QWidget *parent)
    : QMainWindow(parent)
{

}

callback_func::~callback_func()
{

}

FUNC callback_func::print(int a)
{
#ifdef USE_SWITCH
        switch (a) {
        case 0:
            return hello;
            break;
        case 1:
            return bye;
            break;
        case 2:
            return ok;
            break;
        default:
            break;
        }
#else
    FUNC words[3] = {&hello, &bye, &ok};
    FUNC fun = words[a];
    (*fun)(a);
#endif
}

void callback_func::hello(int)
{
    qDebug("hello!");
}
void callback_func::bye(int)
{
    qDebug("bye");
}
void callback_func::ok(int)
{
    qDebug("ok!");
}

#include "callback_func.h"
#include <QApplication>
#define USE_SWITCH

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    callback_func w;
    w.show();

     FUNC fun = w.print(2);//参数可以由用户设置,从而对应触发不同的函数
#ifdef USE_SWITCH
    (*fun)(2);
#else

#endif


    return a.exec();
}

总结下函数指针的作用(借用https://blog.csdn.net/wujiangguizhen/article/details/17153495的部分内容):

1)提供调用的灵活性。设计好了一个函数框架,但是设计初期并不知道自己的函数会被如何使用。比如C的”stdlib”中声明的qsort函数,用来对数值进行排序。显然,顺序还是降序,元素谁大谁小这些问题,库程序员在编写qsort的时候不可能决定。这些问题是要在用户调用这个函数的时候才能够决定。那边qsort如何保证通用性和灵活性呢?采用的办法是让函数的使用者来制定排序规则。于是调用者应该自己设计comparator函数,传给qsort函数。这就在程序设计初期保证了灵活性。尽管使用函数指针使得程序有些难懂,但是这样的牺牲还是值得的。
 
2)提供封装性能。有点面向对象编程的特点。比如设计一个栈结构

typedef struct _c_stack{
               int base_size;
               int point;
               int * base;
               int size;
               int  (*pop)(struct _c_stack *);
               int  (*push)(int,struct _c_stack *);
               int  (*get_top)(struct _c_stack);
}c_stack;
在初始化完之后,用户调用这个结构体上的pop函数,只需要s.pop(&s)即可。即使这个时候,工程内部有另外一个函数名字也叫pop,他们之间是不会发生名字上的冲突的。

原因很简单,因为结构体中的函数指针指向的函数名字可能是

int ugly_stupid_no_one_will_use_this_name_pop(c_stack *)
,只是stack的用户是不知道他在调用s.pop(&s),实际上起作用的是这样一个有着冗长名字的函数。

 函数指针这种避免命名冲突上的额外好处对于一些库函数的编写者是很有意义的,因为库可能被很多的用户在许多不同的环境下使用,这样就能有效的避免冲突而保证库的可用性。

函数指针的好处:

一般的时候用不到,主要还是一个简化结构和程序通用性的问题,也是实现面向对象编程的一种途径。简单的总结为:

1.       实现面向对象编程中的多态性

2.       回调函数


举两个例子:
1. 你现在要实现一个文件系统,但是文件的介质有硬盘和软盘,那么它们各自的读写函数实现肯定是不一样的。
好比  
int a_write(char *data_stream, int LBA);
int b_write(char *data_stream, int LBA);
有一个结构体维护:
typedef int (*write_operation)(char* data, int LBA);
struct {
  write_operation op;
  ...
} file_struct;
最后有一个写函数:
int file_wirte(char *data_stream, int LBA)
{
  return file_struct.op(data_stream, LBA);
}

2. Windows编程中的事件handle函数,即回调函数,在事件队列都是一个函数指针来保存的:
typedef void (*event_handler) (unsigned int para1, unsigned int para2);
struct event {
  unsigned int ev_id;
  event_handler handler;
};

struct event event_queue[MAX_EVENT_SIZE];
程序可以通过扫描这个事件队列来获取每个事件对应的处理函数,然后调用它,即为回调函数。




typedef struct _c_stack{
               int base_size;
               int point;
               int * base;
               int size;
               int  (*pop)(struct _c_stack *);
               int  (*push)(int,struct _c_stack *);
               int  (*get_top)(struct _c_stack);
}c_stack;

猜你喜欢

转载自blog.csdn.net/weixin_39568531/article/details/80008228