Какова точка указателей на функции?

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

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

Ответ 1

Большинство примеров сводятся к обратным вызовам. Вы вызываете функцию f(), передавая адрес другой функции g(), и f() вызывает g() для определенной задачи. Если вместо f() передать h() адрес h(), тогда f() будет вместо этого возвращать h().

В принципе, это способ параметризовать функцию: некоторая часть ее поведения не жестко закодирована в f(), а в функцию обратного вызова. Вызывающие могут f() вести себя по-другому, передавая разные функции обратного вызова. Классика qsort() из стандартной библиотеки 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.

Ответ 2

Хорошо, я обычно использую их (профессионально) в таблицы прыжков (см. также fooobar.com/questions/8205/...).

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

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

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

Ответ 3

Примеры:

  • Пользовательская сортировка/поиск
  • Разные   шаблоны (например, Strategy, Observer).
  • Callbacks

Ответ 4

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

Ответ 5

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

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

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

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

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

Ответ 6

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

Ответ 7

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

Ответ 8

Я собираюсь пойти против текущего здесь.

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

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

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

  • Они могут представлять несколько перегрузок 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;

Но так как вариативные шаблоны и lambdas находятся за углом, я не уверен, что мы будем использовать указатели функций в чистом С++-коде надолго.

Ответ 9

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

Ответ 10

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

Скажите, что вы пишете Бомбермана. Через 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() может быть любой функцией, потому что это указатель на функцию.

Ответ 11

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

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

Литература:

Ответ 12

Я интенсивно использую указатели функций для эмуляции микропроцессоров, которые имеют 1-байтные коды операций. Массив из 256 указателей на функции - это естественный способ реализовать это.

Ответ 13

Одно использование указателя функции может быть там, где мы можем не захотеть модифицировать код, в котором функция вызывает вызов (что означает, что вызов может быть условным и в разных условиях, нам нужно делать разные виды обработки). Здесь указатели на функции очень удобны, поскольку нам не нужно изменять код в том месте, где вызывается функция. Мы просто вызываем функцию с помощью указателя функции с соответствующими аргументами. Указатель функции может быть сделан для условного обозначения различных функций. (Это можно сделать где-то во время фазы инициализации). Более того, приведенная выше модель очень полезна, если мы не в состоянии изменить код, в котором он вызван (предположим, что это API библиотеки, который мы не можем изменить). API использует указатель на функцию для вызова соответствующей пользовательской функции.

Ответ 14

Для языков OO для выполнения полиморфных вызовов за кулисами (это также верно для C до некоторой точки, я думаю).

Кроме того, они очень полезны для ввода другого поведения в другую функцию (foo) во время выполнения. Это делает функцию foo более высокого порядка. Помимо гибкости, это делает код foo более удобочитаемым, поскольку он позволяет вам вытащить из него лишнюю логику "if-else".

Он позволяет много других полезных вещей в Python, таких как генераторы, замыкания и т.д.

Ответ 15

Другая перспектива, помимо других хороших ответов здесь:

В C есть только указатели на функции, нет никаких функций.

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

my_function(my_arg);

то, что вы на самом деле говорите, это "выполнить вызов указателя my_function с указанным аргументом". Вы делаете вызов через указатель функции. Этот распад для функции указателя означает, что следующие команды эквивалентны предыдущему вызову функции:

(&my_function)(my_arg);
(*my_function)(my_arg);
(**my_function)(my_arg);
(&**my_function)(my_arg);
(***my_function)(my_arg);

и т.д. (спасибо @LuuVinhPhuc).

Итак, вы уже используете указатели функций в качестве значений. Очевидно, вы хотели бы иметь переменные для этих значений - и здесь есть все, что использует другой metion: Полиморфизм/настройка (например, в qsort), обратные вызовы, таблицы перехода и т.д.

В С++ вещи немного сложнее, поскольку у нас есть lambdas и объекты с operator() и даже классом std::function, но принцип по-прежнему в основном одинаков.