Просматривая блог Алёны Сагалаевой (http://alenacpp.blogspot.com) обнаружил ссылку на ресурс, посвященный указателям на функцию и обратным вызовам http://www.newty.de/fpt/index.html.
На мой взгляд Ларс Хендель составил замечательное руководство по работе с указателями на функции.
В ближайшее время закончу перевод, а пока что содержание и первый раздел:
1. Введение в Указатели на Функции
1.1 Что такое "Указатель на Функцию"?
1.2 Вводный Пример или "Как Заменить Оператор Switch"
2. Синтаксис Указателей на Функции в C и C++
2.1 Определение Указателя на Функцию
2.2 Соглашение о Вызовах
2.3 Присвоение Адреса Указателю на Функцию(Assign an Address to a Function Pointer)
2.4 Сравнение Указателей на Функции
2.5 Вызов Функции через Указатель на Функцию
2.6 Как передать Указатель на Функцию в качестве Аргумента?
2.7 Как Вернуть Указатель на Функцию?
2.8 Как Использовать Массивы Указателей на Функции?
3. Как Осуществить Обратные Вызовы в C и C++?
3.1 Введение в Концепцию Функций Обратных Вызовов
3.2 Как Выполнить Обратный Вызов в C?
3.3 Код примера использования qsort
3.4 Как Осуществить Обратный Вызов в Статической Функции-Члене?
3.5 Как Осуществить Обратный Вызов в Нестатической Функции-Члене?
Пример А: Указатель, инстанцированный в классе, передается в качестве дополнительного параметра
Пример Б: Указатель, инстанцированный в классе, хранится в глобальной переменной
4. Функторы для инкапсуляции Указателей на Функции в C и C++
4.1 Что такое Функторы?
4.2 Как Имплементировать Функторы?
4.3 Пример Использования Функторов
5. Специальные Ссылки
5.1 Введения в Указатели на Функции
5.2 Обратные Вызовы и Функции
5.3 Разное
1. Введение в Указатели на Функции
1.1 Что такое "Указатель на Функцию"?
1.2 Вводный Пример или "Как Заменить Конструкцию Switch-Case"
Указатели на функции обеспечивают несколько чрезвычайно интересных, эффективных и изящных программных методов. Вы можете использовать их, чтобы заменить switch/if-операторы, реализовать ваше собственное позднее связывание (late-binding) или обратные вызовы (callbacks). К сожалению, вероятно из-за их сложного синтаксиса, в большинстве компьютерных книг и документации к ним относятся весьма неприязненно. Если о них и говорится, то довольно-таки кратко и поверхностно. Они в меньшей степени подвержены ошибкам, чем обычные указатели, вы никогда не будете выделять или освобождать для них память. Все, что вы должны сделать, - это понять, чем они являются и изучить их синтаксис. Но имейте в виду: вы всегда должны спрашивать себя, действительно ли вы нуждаетесь в указателе на функцию. Приятно реализовать собственное позднее связывание, но использование существующих структур C ++ может сделать ваш код удобочитаемым и более понятным. Один из аспектов в случае позднего связывания - время выполнения: если вы вызываете виртуальную функцию, ваша программа должна определить, какую функцию нужно вызвать. Это делается посредством виртуальной таблицы (V-Table), содержащей все возможные функции. Это стоит некоторого времени на каждый вызов, и возможно вы можете сократить время, используя указатели на функции вместо виртуальных функций. Возможно, нет... Кстати: современные компиляторы очень хороши! С моим компилятором Borland экономия времени от вызова виртуальной функции, умножающей два числа типа float, составила приблизительно 2 процента.
1.1 Что такое "Указатель на Функцию"?
Указатели на функции - это указатели, т.е. переменные, которые указывают на адрес функции. Вы должны помнить, что запущенная программа получает определённую область в оперативной памяти. И исполняемый скомпилированный программный код, и используемые переменные, размещаются в этой памяти. Таким образом, функция в коде программы так же как, например, символьное поле, ничто иное, как адрес. Очень важно, как вы, или лучше ваш компилятор/процессор, интерпретируете память, на которую указывает указатель.
1.2 Вводный Пример или "Как Заменить Оператор Switch"
Когда Вы хотите вызвать функцию DoIt() в определенной точке, обозначенной в вашей программе меткой, вы только помещаете вызов функции DoIt() в точке метки в вашем исходном коде. Затем вы компилируете ваш код, и каждый раз, когда ваша программа доходит до метки, вызывается ваша функция. Все Ok. Но что вы можете сделать, если вы не знаете, в какой момент времени должна быть вызвана функция? Что вы делаете, когда хотите решить это во время выполнения? Возможно, вы захотите использовать так называемую Функцию обратного вызова (Callback Function), или выбрать одну функцию из пула возможных функций. Однако, вы также можете решить проблему, используя оператор switch, где вы вызываете функции точно так, как вы этого хотите, используя различные переходы (branches). Но существует другой способ: используйте указатель на функцию!
В следующем примере мы рассмотрим задачу по выполнению одной из четырех основных арифметических операций. Сначала задача решена с использованием оператора switch. Затем демонстрируется, как то же самое может быть сделано с помощью указателя на функцию. Это только пример, и задача настолько проста, что я предполагаю, никто не будет для этого использовать указатель на функцию;-)
//-------------------------------------- ---------------------------------------- ------
// 1.2 Вводный Пример или "Как Заменить Оператор Switch"
// Задача: Выполните одну из четырех основных арифметических операций, определенных
// символами '+', '-,'' * 'или' / '.
// Четыре арифметических операции ... одна из этих функций выбирается
// во время выполнения с помощью switch или указателя на функцию
float Plus (float a, float b) { return a+b; }
float Minus (float a, float b) { return a-b; }
float Multiply(float a, float b) { return a*b; }
float Divide (float a, float b) { return a/b; }
// Решение с помощью оператора switch - <opCode> определяющего, какая операция выполнится
void Switch(float a, float b, char opCode)
{
float result;
// выполнение операции
switch(opCode)
{
case '+' : result = Plus (a, b); break;
case '-' : result = Minus (a, b); break;
case '*' : result = Multiply (a, b); break;
case '/' : result = Divide (a, b); break;
}
cout << "Switch: 2+5=" << result << endl; // отображение результата
}
// Решение с помощью указателя на функцию - <pt2Func>;
// pt2Func указывет на функцию, принимающую два аргумента типа float и возвращающую также float.
// Указатель на функцию "определяет", какая операция будет выполнена.
void Switch_With_Function_Pointer(float a, float b, float (*pt2Func)(float, float))
{
float result = pt2Func(a, b); // вызов через указатель на функцию
cout << "Switch заменён указателем на функцию: 2-5="; // отображение результата
cout << result << endl;
}
// Выполнение кода примера
void Replace_A_Switch()
{
cout << endl << "Выполнение функции 'Replace_A_Switch'" << endl;
Switch(2, 5, /* '+' будет выполнена выбранная функция 'Plus' */ '+');
Switch_With_Function_Pointer(2, 5, /* указатель на функцию 'Minus' */ &Minus);
}
Важное замечание: Указатель на функцию всегда указывает на функцию со строго определённой сигнатурой! Так все функции, к которым вы хотите обращаться через указатель, должны иметь такие же входные параметры и возвращаемое значение (что и в объявлении указателя).
На мой взгляд Ларс Хендель составил замечательное руководство по работе с указателями на функции.
В ближайшее время закончу перевод, а пока что содержание и первый раздел:
1. Введение в Указатели на Функции
1.1 Что такое "Указатель на Функцию"?
1.2 Вводный Пример или "Как Заменить Оператор Switch"
2. Синтаксис Указателей на Функции в C и C++
2.1 Определение Указателя на Функцию
2.2 Соглашение о Вызовах
2.3 Присвоение Адреса Указателю на Функцию(Assign an Address to a Function Pointer)
2.4 Сравнение Указателей на Функции
2.5 Вызов Функции через Указатель на Функцию
2.6 Как передать Указатель на Функцию в качестве Аргумента?
2.7 Как Вернуть Указатель на Функцию?
2.8 Как Использовать Массивы Указателей на Функции?
3. Как Осуществить Обратные Вызовы в C и C++?
3.1 Введение в Концепцию Функций Обратных Вызовов
3.2 Как Выполнить Обратный Вызов в C?
3.3 Код примера использования qsort
3.4 Как Осуществить Обратный Вызов в Статической Функции-Члене?
3.5 Как Осуществить Обратный Вызов в Нестатической Функции-Члене?
Пример А: Указатель, инстанцированный в классе, передается в качестве дополнительного параметра
Пример Б: Указатель, инстанцированный в классе, хранится в глобальной переменной
4. Функторы для инкапсуляции Указателей на Функции в C и C++
4.1 Что такое Функторы?
4.2 Как Имплементировать Функторы?
4.3 Пример Использования Функторов
5. Специальные Ссылки
5.1 Введения в Указатели на Функции
5.2 Обратные Вызовы и Функции
5.3 Разное
1. Введение в Указатели на Функции
1.1 Что такое "Указатель на Функцию"?
1.2 Вводный Пример или "Как Заменить Конструкцию Switch-Case"
Указатели на функции обеспечивают несколько чрезвычайно интересных, эффективных и изящных программных методов. Вы можете использовать их, чтобы заменить switch/if-операторы, реализовать ваше собственное позднее связывание (late-binding) или обратные вызовы (callbacks). К сожалению, вероятно из-за их сложного синтаксиса, в большинстве компьютерных книг и документации к ним относятся весьма неприязненно. Если о них и говорится, то довольно-таки кратко и поверхностно. Они в меньшей степени подвержены ошибкам, чем обычные указатели, вы никогда не будете выделять или освобождать для них память. Все, что вы должны сделать, - это понять, чем они являются и изучить их синтаксис. Но имейте в виду: вы всегда должны спрашивать себя, действительно ли вы нуждаетесь в указателе на функцию. Приятно реализовать собственное позднее связывание, но использование существующих структур C ++ может сделать ваш код удобочитаемым и более понятным. Один из аспектов в случае позднего связывания - время выполнения: если вы вызываете виртуальную функцию, ваша программа должна определить, какую функцию нужно вызвать. Это делается посредством виртуальной таблицы (V-Table), содержащей все возможные функции. Это стоит некоторого времени на каждый вызов, и возможно вы можете сократить время, используя указатели на функции вместо виртуальных функций. Возможно, нет... Кстати: современные компиляторы очень хороши! С моим компилятором Borland экономия времени от вызова виртуальной функции, умножающей два числа типа float, составила приблизительно 2 процента.
1.1 Что такое "Указатель на Функцию"?
Указатели на функции - это указатели, т.е. переменные, которые указывают на адрес функции. Вы должны помнить, что запущенная программа получает определённую область в оперативной памяти. И исполняемый скомпилированный программный код, и используемые переменные, размещаются в этой памяти. Таким образом, функция в коде программы так же как, например, символьное поле, ничто иное, как адрес. Очень важно, как вы, или лучше ваш компилятор/процессор, интерпретируете память, на которую указывает указатель.
1.2 Вводный Пример или "Как Заменить Оператор Switch"
Когда Вы хотите вызвать функцию DoIt() в определенной точке, обозначенной в вашей программе меткой, вы только помещаете вызов функции DoIt() в точке метки в вашем исходном коде. Затем вы компилируете ваш код, и каждый раз, когда ваша программа доходит до метки, вызывается ваша функция. Все Ok. Но что вы можете сделать, если вы не знаете, в какой момент времени должна быть вызвана функция? Что вы делаете, когда хотите решить это во время выполнения? Возможно, вы захотите использовать так называемую Функцию обратного вызова (Callback Function), или выбрать одну функцию из пула возможных функций. Однако, вы также можете решить проблему, используя оператор switch, где вы вызываете функции точно так, как вы этого хотите, используя различные переходы (branches). Но существует другой способ: используйте указатель на функцию!
В следующем примере мы рассмотрим задачу по выполнению одной из четырех основных арифметических операций. Сначала задача решена с использованием оператора switch. Затем демонстрируется, как то же самое может быть сделано с помощью указателя на функцию. Это только пример, и задача настолько проста, что я предполагаю, никто не будет для этого использовать указатель на функцию;-)
//--------------------------------------
// 1.2 Вводный Пример или "Как Заменить Оператор Switch"
// Задача: Выполните одну из четырех основных арифметических операций, определенных
// символами '+', '-,'' * 'или' / '.
// Четыре арифметических операции ... одна из этих функций выбирается
// во время выполнения с помощью switch или указателя на функцию
float Plus (float a, float b) { return a+b; }
float Minus (float a, float b) { return a-b; }
float Multiply(float a, float b) { return a*b; }
float Divide (float a, float b) { return a/b; }
// Решение с помощью оператора switch - <opCode> определяющего, какая операция выполнится
void Switch(float a, float b, char opCode)
{
float result;
// выполнение операции
switch(opCode)
{
case '+' : result = Plus (a, b); break;
case '-' : result = Minus (a, b); break;
case '*' : result = Multiply (a, b); break;
case '/' : result = Divide (a, b); break;
}
cout << "Switch: 2+5=" << result << endl; // отображение результата
}
// Решение с помощью указателя на функцию - <pt2Func>;
// pt2Func указывет на функцию, принимающую два аргумента типа float и возвращающую также float.
// Указатель на функцию "определяет", какая операция будет выполнена.
void Switch_With_Function_Pointer(float a, float b, float (*pt2Func)(float, float))
{
float result = pt2Func(a, b); // вызов через указатель на функцию
cout << "Switch заменён указателем на функцию: 2-5="; // отображение результата
cout << result << endl;
}
// Выполнение кода примера
void Replace_A_Switch()
{
cout << endl << "Выполнение функции 'Replace_A_Switch'" << endl;
Switch(2, 5, /* '+' будет выполнена выбранная функция 'Plus' */ '+');
Switch_With_Function_Pointer(2, 5, /* указатель на функцию 'Minus' */ &Minus);
}
Важное замечание: Указатель на функцию всегда указывает на функцию со строго определённой сигнатурой! Так все функции, к которым вы хотите обращаться через указатель, должны иметь такие же входные параметры и возвращаемое значение (что и в объявлении указателя).
- Mood:
relaxed

