зачем нам вызывать эти функции во время выполнения с помощью указателей на функции. мы также можем позвонить им напрямую

Прочитав немного об указателях функций и обратных вызовах, я не понимаю их основной цели. Мне кажется, что вместо прямого вызова функции мы используем указатель на эту функцию для ее вызова. Кто-нибудь может объяснить мне обратные вызовы и указатели функций? Почему обратный вызов происходит, когда мы используем указатели на функции, потому что кажется, что мы просто вызываем функцию через указатель на нее, а не напрямую?

Спасибо

ps: здесь было задано несколько вопросов относительно обратных вызовов и указателей функций, но они недостаточно объясняют мою проблему.


person polapts    schedule 15.08.2011    source источник


Ответы (4)


Что такое функция обратного вызова?
Проще говоря, функция обратного вызова – это функция, которая не вызывается программистом явно. Вместо этого существует некий механизм, который постоянно ожидает возникновения событий и вызывает выбранные функции в ответ на определенные события.
Этот механизм обычно используется, когда выполнение операции (функции) может занять много времени и вызывающая сторона функция не хочет ждать, пока операция завершится, но хочет получить информацию о результате операции. Как правило, функции обратного вызова помогают реализовать такой асинхронный механизм, в котором вызывающий абонент регистрируется, чтобы получить информацию о результате длительной обработки и продолжения других операций, а на более позднем этапе время, вызывающий получает информацию о результате.

Практический пример:
обработка событий 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  

Надеюсь это поможет!

Примечание. Это из одного из моих предыдущих ответов, здесь

person Alok Save    schedule 15.08.2011
comment
Спасибо Алс. Это объясняет использование указателя на функцию, но обратный вызов все еще неуловим. Здесь происходит обратный вызов. Кажется, мы просто вызываем функцию, которую хотели вызвать. - person polapts; 15.08.2011
comment
Дело в том, что в приведенном выше примере все, кроме Кошки, Собаки и Птицы, могут быть частью чего-то, что вы не писали сами, исходного кода чего у вас нет или что вы хотели написать в общем виде. т. е. что вам не нужно изменять его, когда вдруг вы также решите вызвать Рыбу (или любого из других тысяч животных) - person user52875; 15.08.2011
comment
@user52875 user52875 еще одна вещь, которую я понял, что указатель на функцию - это способ достичь абстракции .. Я понял вашу точку зрения. Но как насчет обратного вызова? - person polapts; 15.08.2011
comment
@polapts: Дело в том, что коду цикла событий не нужно ничего знать о конкретных зарегистрированных функциях. Таким образом, этот код всегда остается неизменным. пользователь этого кода может регистрировать любые функции, которые ему нравятся, в качестве обратных вызовов, и цикл событий будет вызывать их в нужное время. - person Oliver Charlesworth; 15.08.2011

Одна очень поразительная причина, по которой нам нужны указатели на функции, заключается в том, что они позволяют нам вызывать функцию, о которой автор вызывающего кода (то есть мы) не знает! Обратный звонок — классический пример; автор 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);
person Kerrek SB    schedule 15.08.2011

Это для развязки. Посмотрите на sqlite3_exec() — он принимает указатель обратного вызова, который вызывается для каждой полученной строки. SQLite не заботится о том, что делает ваш обратный вызов, ему нужно только знать, как его вызвать.

Теперь вам не нужно перекомпилировать SQLite каждый раз, когда ваш обратный вызов изменяется. Вы можете скомпилировать SQLite один раз, а затем просто перекомпилировать свой код и либо перекомпоновать статически, либо просто перезапустить и перекомпоновать динамически.

person sharptooth    schedule 15.08.2011
comment
Посмотрите на qsort в стандартной библиотеке. - person Chris Lutz; 15.08.2011
comment
Лучшим примером будет предикат std::sort, который намного чище. - person MSalters; 15.08.2011

Это также позволяет избежать конфликта имен. Если у вас есть 2 библиотеки, обе выполняют сортировку, и обе ожидают, что вы определите функцию с именем sort_criteria, которую они могут вызывать, как бы вы отсортировали 2 разных типа объектов с одной и той же функцией?

Это быстро усложнилось бы после всех if и переключателей в функции sort_criteria, с обратными вызовами вы можете указать свою собственную функцию (с их удобным для интерпретации именем) для этих функций сортировки.

person RedX    schedule 15.08.2011