Я испытываю затруднения при наблюдении утилиты указателей функции. Я предполагаю, что это может быть полезно в некоторых случаях (они существуют, в конце концов), но я не могу думать о случае, где это лучше или неизбежно использовать указатель функции.
Вы могли дать некоторый пример хорошего использования указателей функции (в C или C++)?
Примеры:
Ну, я обычно использую их (профессионально) в таблицах переходов (см. Также этот вопрос StackOverflow ).
Таблицы переходов обычно (но не исключительно) используются в конечных автоматах для управления данными. Вместо вложенного переключателя / case
switch (state)
case A:
switch (event):
case e1: ....
case e2: ....
case B:
switch (event):
case e3: ....
case e1: ....
вы можете создать двумерный массив или указатели функций и просто вызвать handleEvent [state] [event]
В C классическим использованием является функция qsort , где четвертый параметр - это указатель на функцию, используемую для выполнения упорядочивания в сортировке. В C ++ для такого рода вещей обычно используются функторы (объекты, похожие на функции).
Здесь я пойду против течения.
В C указатели на функции - единственный способ реализовать настройку, потому что здесь нет объектно-ориентированного программирования.
В C ++ для одного и того же результата можно использовать указатели на функции или функторы (объекты функций).
Функторы имеют ряд преимуществ перед указателями на исходные функции из-за их объектной природы, а именно:
()
lambda
и bind
) Я лично предпочитаю функторы указателям на функции (несмотря на шаблонный код), в основном потому, что синтаксис для указатели на функции могут легко запутаться (из Учебного пособия по указателям функций ):
typedef float(*pt2Func)(float, float);
// defines a symbol pt2Func, pointer to a (float, float) -> float function
typedef int (TMyClass::*pt2Member)(float, char, char);
// defines a symbol pt2Member, pointer to a (float, char, char) -> int function
// belonging to the class TMyClass
Единственный раз, когда я когда-либо видел, как указатели на функции используются там, где не могут работать функторы, был в Boost.Spirit. Они полностью злоупотребили синтаксисом, чтобы передать произвольное количество параметров как один параметр шаблона.
typedef SpecialClass<float(float,float)> class_type;
Но поскольку вариативные шаблоны и лямбды уже не за горами, я не уверен, что мы будем долго использовать указатели на функции в чистом коде C ++.
Указатели функций могут использоваться в C для создания интерфейса для программирования. В зависимости от конкретной функциональности, необходимой во время выполнения, указателю функции может быть назначена другая реализация.
Недавно я использовал указатели на функции для создания уровня абстракции.
У меня есть программа, написанная на чистом C, которая работает во встроенных системах. Он поддерживает несколько вариантов оборудования. В зависимости от оборудования, на котором я работаю, ему необходимо вызывать разные версии некоторых функций.
Во время инициализации программа определяет, на каком оборудовании она работает, и заполняет указатели функций. Все подпрограммы более высокого уровня в программе просто вызывают функции, на которые ссылаются указатели. Я могу добавить поддержку новых вариантов оборудования, не затрагивая процедуры более высокого уровня.
Раньше я использовал операторы switch / case для выбора правильных версий функций, но это стало непрактичным, поскольку программа расширялась и поддерживала все больше и больше вариантов оборудования. Мне пришлось добавлять операторы case повсюду.
Я также пробовал использовать промежуточные функциональные уровни, чтобы выяснить, какую функцию использовать, но они мало помогли. Мне все еще приходилось обновлять операторы case в нескольких местах всякий раз, когда мы добавляли новый вариант. С указателями функций мне нужно только изменить функцию инициализации.
«Классическим» примером полезности указателей на функции является функция библиотеки C qsort ()
, которая реализует быструю сортировку. Чтобы быть универсальным для любых структур данных, которые может придумать пользователь, требуется пара недействительных указателей на сортируемые данные и указатель на функцию, которая знает, как сравнивать два элемента этих структур данных. Это позволяет нам создать нашу функцию выбора для задания и фактически даже позволяет выбирать функцию сравнения во время выполнения, например для сортировки по возрастанию или убыванию.
Большинство примеров сводятся к обратным вызовам : Вы вызываете функцию f ()
, передавая адрес другой функции g ()
, а f ()
вызывает g ()
для какой-то конкретной задачи. Если вы передадите f ()
адрес h ()
, тогда f ()
вместо этого перезвонит h ()
.
По сути, это способ параметризации функции: некоторая часть ее поведения жестко не запрограммирована в f ()
, а в функции обратного вызова. Вызывающие могут заставить f ()
вести себя по-разному, передавая разные функции обратного вызова. Классическим является qsort ()
из стандартной библиотеки C, которая принимает свой критерий сортировки как указатель на функцию сравнения.
В C ++ это часто делается с помощью объектов функций (также называемых функторами). Это объекты, которые перегружают оператор вызова функции, поэтому вы можете вызывать их, как если бы они были функцией. Пример:
class functor {
public:
void operator()(int i) {std::cout << "the answer is: " << i << '\n';}
};
functor f;
f(42);
Идея заключается в том, что, в отличие от указателя на функцию, объект функции может нести не только алгоритм, но и данные:
class functor {
public:
functor(const std::string& prompt) : prompt_(prompt) {}
void operator()(int i) {std::cout << prompt_ << i << '\n';}
private:
std::string prompt_;
};
functor f("the answer is: ");
f(42);
Еще одно преимущество состоит в том, что иногда проще встроить вызовы к объектам функций, чем вызовы через указатели на функции. По этой причине сортировка в C ++ иногда выполняется быстрее, чем в C.
Согласен со всем вышесказанным, плюс..... Когда вы загружаете dll динамически во время выполнения, вам понадобятся указатели функций для вызова функций.