Какой смысл указателей на функции? - PullRequest
83 голосов
/ 07 апреля 2010

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

Ответы [ 16 ]

102 голосов
/ 07 апреля 2010

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

39 голосов
/ 07 апреля 2010

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

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

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

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

23 голосов
/ 07 апреля 2010

Примеры:

  1. Настраиваемая сортировка / поиск
  2. Различные шаблоны (например, стратегия, наблюдатель)
  3. Обратные вызовы
10 голосов
/ 07 апреля 2010

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

6 голосов
/ 07 апреля 2010

Я собираюсь пойти против течения здесь.

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

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

Функторы имеют ряд преимуществ по сравнению с необработанными указателями на функции из-за их природы объекта, а именно:

  • Они могут представлять несколько перегрузок operator()
  • Они могут иметь состояние / ссылку на существующие переменные
  • Их можно построить на месте (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 ++.

5 голосов
/ 07 апреля 2010

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

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

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

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

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

5 голосов
/ 07 апреля 2010

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

5 голосов
/ 07 апреля 2010

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

3 голосов
/ 28 июня 2016

Как и Rich , как было сказано выше, указатели на функции в Windows очень часто ссылаются на некоторый адрес, в котором хранится функция.

Когда вы программируете в C language на платформе Windows, вы в основном загружаете некоторый DLL-файл в первичную память (используя LoadLibrary), и для использования функций, хранящихся в DLL, вам нужно создать указатели на функции и указать на эти адреса (используя GetProcAddress).

Ссылка:

2 голосов
/ 14 ноября 2012

Я использовал их в основном CALLBACKS: когда вам нужно сохранить информацию о функции в , позвоните позже .

Скажи, что ты пишешь Bomberman. Через 5 секунд после того, как человек сбросит бомбу, она должна взорваться (вызвать функцию explode()).

Теперь есть 2 способа сделать это. Один из способов - «проверить» все бомбы на экране, чтобы увидеть, готовы ли они взорваться в основном цикле.

foreach bomb in game 
   if bomb.boomtime()
       bomb.explode()

Другой способ - добавить обратный вызов к вашей системе часов. Когда бомба заложена, вы добавляете обратный вызов, чтобы она вызывала bomb.explode (), когда настало время .

// user placed a bomb
Bomb* bomb = new Bomb()
make callback( function=bomb.explode, time=5 seconds ) ;

// IN the main loop:
foreach callback in callbacks
    if callback.timeToRun
         callback.function()

Здесь callback.function() может быть любой функцией , потому что это указатель на функцию.

...