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

时间:2011-08-15 08:57:00

标签: c++ c callback function-pointers

读过一些关于函数指针和回调的内容后,我无法理解它的基本用途。对我而言,它看起来不是直接调用函数,而是使用指向该函数的指针来调用它。任何人都可以解释一下我的回调和函数指针吗?当我们使用函数指针时,如何进行回调,因为我们只是通过指向它的指针调用函数而不是直接调用?

由于

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

4 个答案:

答案 0 :(得分:6)

什么是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 found\n";
            }
        }
    }
}

void Cat()
{
   std::cout << "Cat\n";
}

void Dog()
{
    std::cout << "Dog\n";
}

void Bird()
{
    std::cout << "Bird\n";
}

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  

希望这有帮助!

注意:这是我以前的一个答案, here 功能

答案 1 :(得分:4)

为什么我们需要函数指针的一个非常明显的原因是它们允许我们调用一个函数,调用代码的作者(那是我们)不知道!回拨是一个典型的例子; 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);

答案 2 :(得分:1)

适用于decoupling。查看sqlite3_exec() - 它接受为每个检索到的行调用的回调指针。 SQLite不关心你的回调做什么,它只需要知道如何调用它。

现在,每次回调更改时都不需要重新编译SQLite。您可能只编译了一次SQLite,然后重新编译代码并静态重新链接或只是重新启动并动态重新链接。

答案 3 :(得分:1)

它还可以避免名称冲突。如果你有2个库,都进行排序,并且都希望你定义一个他们可以调用的名为sort_criteria的函数,你如何用相同的函数对2个不同的对象类型进行排序?

sort_criteria函数中的所有if和switch之后会很快变得复杂,使用回调可以为这些排序函数指定自己的函数(具有很好的解释名称)。