为什么我们需要在运行时使用函数指针调用这些函数?我们也可以直接打电话给他们

why do we need to call these functions at run time using function pointers. we can as well call them directly

本文关键字:函数 我们 也可以 他们 打电话 指针 运行时 为什么 调用      更新时间:2023-10-16

在阅读了一些关于函数指针和回调的内容后,我无法理解它们的基本目的。对我来说,它看起来就像不是直接调用函数,而是使用指向该函数的指针来调用它。有人能解释一下回调函数和函数指针吗?当我们使用函数指针时,为什么会发生回调,因为我们似乎只是通过指向它的指针调用函数,而不是直接调用?

感谢

ps:这里有一些关于回调和函数指针的问题,但它们不能充分解释我的问题

什么是Callbak函数?
简单来说,回调函数是程序员不显式调用的函数。相反,有一些机制可以持续等待事件的发生,并且它将调用选定的函数来响应特定的事件。此机制通常用于操作(函数)可能需要很长时间才能执行,并且函数的调用者不想等待操作完成,但确实希望知道操作的结果。通常,回调函数帮助实现这样的异步机制,其中调用方注册以获得有关耗时处理和连续其他操作的结果的动画,而在稍后的时间点,调用方获得结果的通知。

一个实际的例子:
Windows事件处理:
实际上,所有的Windows程序都设置了一个事件循环,通过调用函数使程序响应特定的事件(如按钮按下,选择一个复选框,窗口获得焦点)。方便的事情是,程序员可以指定当(比如说)按下特定按钮时调用什么函数,即使不可能指定何时按下按钮。被调用的函数被称为回调函数。

一个源代码说明:

//warning:  Mind compiled code, intended to illustrate the mechanism    
#include <map>
typedef void (*Callback)();
std::map<int, Callback>  callback_map;
void RegisterCallback(int event, Callback function)
{
    callback_map[event] = function;
}
bool finished = false;
int GetNextEvent()
{
    static int i = 0;
    ++i;
    if (i == 5) finished = false;
}
void EventProcessor()
{
    int event;
    while (!finished)
    {
        event = GetNextEvent();
        std::map<int, Callback>::const_iterator it = callback_map.find(event);
        if (it != callback_map.end())    // if a callback is registered for event
        {
            Callback function = *it;
            if (function)   
            {
                (*function)();
            }
            else
            {
               std::cout << "No callback foundn";
            }
        }
    }
}
void Cat()
{
   std::cout << "Catn";
}
void Dog()
{
    std::cout << "Dogn";
}
void Bird()
{
    std::cout << "Birdn";
}
int main()
{
    RegisterCallBack(1, Cat);
    RegisterCallback(2, Dog);
    RegisterCallback(3, Cat);
    RegisterCallback(4, Bird);
    RegisterCallback(5, Cat);
    EventProcessor(); 
    return 0;
}

上面的命令将输出如下内容:

Cat  
Dog   
Cat  
Bird  
Cat  

希望这对你有帮助!

<一口>注意:这是我以前的一个答案,在这里

我们需要函数指针的一个非常明显的原因是,它们允许我们调用调用代码的作者(即我们)不知道的函数!回调是一个经典的例子;qsort()的作者不知道也不关心你如何比较元素,她只是写了一个泛型算法,由你来提供比较函数。

但是对于另一个重要的,广泛使用的场景,考虑动态加载库-这里我指的是在运行时加载。在编写程序时,您不知道运行时加载的库中存在哪些函数。您可以从用户输入中读取文本字符串,然后打开用户指定的库并执行用户指定的函数!你只能通过指针来引用这样的函数。

这里有一个简单的例子;我希望它能说服你,你不能放弃指针!

typedef int (*myfp)();  // function pointer type
const char * libname = get_library_name_from_user();
const char * funname = get_function_name_from_user();
void * libhandle = dlopen(libname, RTLD_NOW);  // load the library
myfp fun = (myfp) dlsym(libhandle, funname);   // get our mystery function...
const int result = myfp();                     // ... and call the function
                                               // -- we have no idea which one!
printf("Your function "%s:%s" returns %i.n", libname, funname, result);

这是为了解耦。看看sqlite3_exec()——它接受一个回调指针,为检索到的每一行调用该指针。SQLite不关心你的回调做什么,它只需要知道如何调用它。

现在您不需要在每次回调更改时重新编译SQLite。你可以编译一次SQLite,然后重新编译你的代码,或者静态重链接,或者只是重启和动态重链接。

这也避免了名称冲突。如果你有两个库,都做排序,都希望你定义一个叫做sort_criteria的函数,他们可以调用,你怎么用同一个函数排序2个不同的对象类型?

sort_criteria函数中的所有if和开关之后,它会很快变得复杂,通过回调,您可以将自己的函数(具有易于解释的名称)指定给这些排序函数。

相关文章: