Какой смысл указателей функции?

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

Вы могли дать некоторый пример хорошего использования указателей функции (в C или C++)?

85
задан Nate 14 November 2012 в 19:31
поделиться

9 ответов

Примеры:

  1. Пользовательская сортировка / поиск
  2. Различные шаблоны (например, стратегия, наблюдатель)
  3. Обратные вызовы
24
ответ дан 24 November 2019 в 08:13
поделиться

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

Таблицы переходов обычно (но не исключительно) используются в конечных автоматах для управления данными. Вместо вложенного переключателя / case

  switch (state)
     case A:
       switch (event):
         case e1: ....
         case e2: ....
     case B:
       switch (event):
         case e3: ....
         case e1: ....

вы можете создать двумерный массив или указатели функций и просто вызвать handleEvent [state] [event]

39
ответ дан 24 November 2019 в 08:13
поделиться

В C классическим использованием является функция qsort , где четвертый параметр - это указатель на функцию, используемую для выполнения упорядочивания в сортировке. В C ++ для такого рода вещей обычно используются функторы (объекты, похожие на функции).

5
ответ дан 24 November 2019 в 08:13
поделиться

Здесь я пойду против течения.

В 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 ++.

6
ответ дан 24 November 2019 в 08:13
поделиться

Указатели функций могут использоваться в C для создания интерфейса для программирования. В зависимости от конкретной функциональности, необходимой во время выполнения, указателю функции может быть назначена другая реализация.

2
ответ дан 24 November 2019 в 08:13
поделиться

Недавно я использовал указатели на функции для создания уровня абстракции.

У меня есть программа, написанная на чистом C, которая работает во встроенных системах. Он поддерживает несколько вариантов оборудования. В зависимости от оборудования, на котором я работаю, ему необходимо вызывать разные версии некоторых функций.

Во время инициализации программа определяет, на каком оборудовании она работает, и заполняет указатели функций. Все подпрограммы более высокого уровня в программе просто вызывают функции, на которые ссылаются указатели. Я могу добавить поддержку новых вариантов оборудования, не затрагивая процедуры более высокого уровня.

Раньше я использовал операторы switch / case для выбора правильных версий функций, но это стало непрактичным, поскольку программа расширялась и поддерживала все больше и больше вариантов оборудования. Мне пришлось добавлять операторы case повсюду.

Я также пробовал использовать промежуточные функциональные уровни, чтобы выяснить, какую функцию использовать, но они мало помогли. Мне все еще приходилось обновлять операторы case в нескольких местах всякий раз, когда мы добавляли новый вариант. С указателями функций мне нужно только изменить функцию инициализации.

5
ответ дан 24 November 2019 в 08:13
поделиться

«Классическим» примером полезности указателей на функции является функция библиотеки C qsort () , которая реализует быструю сортировку. Чтобы быть универсальным для любых структур данных, которые может придумать пользователь, требуется пара недействительных указателей на сортируемые данные и указатель на функцию, которая знает, как сравнивать два элемента этих структур данных. Это позволяет нам создать нашу функцию выбора для задания и фактически даже позволяет выбирать функцию сравнения во время выполнения, например для сортировки по возрастанию или убыванию.

10
ответ дан 24 November 2019 в 08:13
поделиться

Большинство примеров сводятся к обратным вызовам : Вы вызываете функцию 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.

106
ответ дан 24 November 2019 в 08:13
поделиться

Согласен со всем вышесказанным, плюс..... Когда вы загружаете dll динамически во время выполнения, вам понадобятся указатели функций для вызова функций.

6
ответ дан 24 November 2019 в 08:13
поделиться