Указатель функции

редактировать

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

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

Указатели функций поддерживаются языками программирования третьего поколения (такими как PL / I, COBOL, Fortran, dBASE dBL и C ) и объектно-ориентированными языками программирования (такими как C ++, C # и D ).

СОДЕРЖАНИЕ
  • 1 Простые указатели на функции
    • 1.1 Пример на C
  • 2 Функторы
  • 3 Указатели на методы
  • 4 В C ++
    • 4.1 Указатели на функции-члены в C ++
  • 5 Альтернативный синтаксис C и C ++
    • 5.1 C и C ++
    • 5.2 C ++
  • 6 См. Также
  • 7 ссылки
  • 8 Внешние ссылки
Указатели на простые функции

Самая простая реализация указателя функции (или подпрограммы) - это переменная, содержащая адрес функции в исполняемой памяти. Старые языки третьего поколения, такие как PL / I и COBOL, а также более современные языки, такие как Pascal и C, обычно реализуют указатели на функции таким образом.

Пример на C

См. Также: § Альтернативный синтаксис C и C ++

Следующая программа на C иллюстрирует использование двух указателей на функции:

  • func1 принимает один параметр двойной точности (double) и возвращает другой двойной, и назначается функции, которая преобразует сантиметры в дюймы.
  • func2 принимает указатель на массив константных символов, а также целое число и возвращает указатель на символ, и назначается функции обработки строки C, которая возвращает указатель на первое вхождение данного символа в массив символов.
#include lt;stdio.hgt; /* for printf */ #include lt;string.hgt; /* for strchr */ double cm_to_inches(double cm) { return cm / 2.54; } // "strchr" is part of the C string handling (i.e., no need for declaration) // See https://en.wikipedia.org/w/C_string_handling#Functions int main(void) { double (*func1)(double) = cm_to_inches; char * (*func2)(const char *, int) = strchr; printf("%f %s", func1(15.0), func2("Wikipedia", 'p')); /* prints "5.905512 pedia" */ return 0; }

Следующая программа использует указатель функции для вызова одной из двух функций ( sinили cos) косвенно из другой функции ( compute_sumвычисляя приближение интегрирования Римана функции). Программа работает, имея функцию mainвызова функции compute_sumдважды, передавая ей указатель на библиотечную функцию sinв первый раз и указатель на функцию cosво второй раз. Функция, compute_sumв свою очередь, вызывает одну из двух функций косвенно, разыменовывая аргумент указателя функции funcpнесколько раз, складывая значения, возвращаемые вызванной функцией, и возвращая полученную сумму. Две суммы записываются в стандартный вывод с помощью main.

#include lt;math.hgt; #include lt;stdio.hgt; // Function taking a function pointer as an argument double compute_sum(double (*funcp)(double), double lo, double hi) {  double sum = 0.0;  // Add values returned by the pointed-to function '*funcp'  int i;  for (i = 0; i lt;= 100; i++) {   // Use the function pointer 'funcp' to invoke the function   double x = i / 100.0 * (hi - lo) + lo;   double y = funcp(x);   sum += y;  }  return sum / 101.0 * (hi - lo); } double square(double x) {  return x * x; } int main(void) {  double sum;  // Use standard library function 'sin()' as the pointed-to function  sum = compute_sum(sin, 0.0, 1.0);  printf("sum(sin): %g\n", sum);  // Use standard library function 'cos()' as the pointed-to function  sum = compute_sum(cos, 0.0, 1.0);  printf("sum(cos): %g\n", sum);  // Use user-defined function 'square()' as the pointed-to function  sum = compute_sum(square, 0.0, 1.0);  printf("sum(square): %g\n", sum);  return 0; }
Функторы
Основная статья: Функциональный объект

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

Многие «чистые» объектно-ориентированные языки не поддерживают указатели на функции. Что-то подобное можно реализовать в этих языках, используя ссылки на интерфейсы, которые определяют единственный метод (функцию-член). Такие языки интерфейса командной строки, как C # и Visual Basic.NET, реализуют типобезопасные указатели на функции с делегатами.

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

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

Указатели методов

C ++ включает поддержку объектно-ориентированного программирования, поэтому классы могут иметь методы (обычно называемые функциями-членами). Нестатические функции-члены (методы экземпляра) имеют неявный параметр ( указатель this ), который является указателем на объект, над которым он работает, поэтому тип объекта должен быть включен как часть типа указателя функции. Затем метод используется для объекта этого класса с помощью одного из операторов «указатель на член»: .*или -gt;*(для объекта или указателя на объект, соответственно).

Хотя указатели на функции в C и C ++ могут быть реализованы как простые адреса, поэтому обычно sizeof(Fx)==sizeof(void *)указатели на элементы в C ++ иногда реализуются как «жирные указатели», обычно в два или три раза превышающие размер простого указателя на функцию, чтобы иметь дело с виртуальными методы и виртуальное наследование.

В C ++

В C ++, помимо метода, используемого в C, также можно использовать шаблон класса стандартной библиотеки C ++ std:: function, экземпляры которого являются объектами функций:

#include lt;iostreamgt; #include lt;functionalgt; static double derivative(const std::functionlt;double(double)gt; amp;f, double x0, double eps) { double eps2 = eps / 2; double lo = x0 - eps2; double hi = x0 + eps2; return (f(hi) - f(lo)) / eps; } static double f(double x) { return x * x; } int main() { double x = 1; std::cout lt;lt; "d/dx(x ^ 2) [@ x = " lt;lt; x lt;lt; "] = " lt;lt; derivative(f, x, 1e-5) lt;lt; std::endl; return 0; }

Указатели на функции-члены в C ++

См. Также: § Альтернативный синтаксис C и C ++

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

#include lt;iostreamgt; using namespace std; class Foo { public: int add(int i, int j) { return i+j; } int mult(int i, int j) { return i*j; } static int negate(int i) { return -i; } }; int bar1(int i, int j, Foo* pFoo, int(Foo::*pfn)(int,int)) { return (pFoo-gt;*pfn)(i,j); } typedef int(Foo::*Foo_pfn)(int,int); int bar2(int i, int j, Foo* pFoo, Foo_pfn pfn) { return (pFoo-gt;*pfn)(i,j); } typedef int(*PFN)(int); int bar3(int i, PFN pfn) { return pfn(i); } int main() { Foo foo; cout lt;lt; "Foo::add(2,4) = " lt;lt; bar1(2,4, amp;foo, amp;Foo::add) lt;lt; endl; cout lt;lt; "Foo::mult(3,5) = " lt;lt; bar2(3,5, amp;foo, amp;Foo::mult) lt;lt; endl; cout lt;lt; "Foo::negate(6) = " lt;lt; bar3(6, amp;Foo::negate) lt;lt; endl; return 0; }
Альтернативный синтаксис C и C ++

Приведенный выше синтаксис C и C ++ является каноническим, используемым во всех учебниках, но его трудно читать и объяснять. Даже в приведенных выше typedefпримерах используется этот синтаксис. Однако каждый компилятор C и C ++ поддерживает более ясный и лаконичный механизм объявления указателей на функции: используйте typedef, но не сохраняйте указатель как часть определения. Обратите внимание, что единственный способ использования этого типа typedef- указатель, но это подчеркивает его указатель.

C и C ++

// This declares 'F', a function that accepts a 'char' and returns an 'int'. Definition is elsewhere. int F(char c); // This defines 'Fn', a type of function that accepts a 'char' and returns an 'int'. typedef int Fn(char c); // This defines 'fn', a variable of type pointer-to-'Fn', and assigns the address of 'F' to it. Fn *fn = amp;F;  // Note 'amp;' not required - but it highlights what is being done. // This calls 'F' using 'fn', assigning the result to the variable 'a' int a = fn('A'); // This defines 'Call', a function that accepts a pointer-to-'Fn', calls it, and returns the result int Call(Fn *fn, char c) { return fn(c); } // Call(fn, c) // This calls function 'Call', passing in 'F' and assigning the result to 'call' int call = Call(amp;F, 'A'); // Again, 'amp;' is not required // LEGACY: Note that to maintain existing code bases, the above definition style can still be used first; // then the original type can be defined in terms of it using the new style. // This defines 'PFn', a type of pointer-to-type-Fn. typedef Fn *PFn; // 'PFn' can be used wherever 'Fn *' can PFn pfn = F; int CallP(PFn fn, char c);

C ++

В этих примерах используются приведенные выше определения. В частности, обратите внимание, что приведенное выше определение для Fnможет использоваться в определениях указателя на функцию-член:

// This defines 'C', a class with similar static and member functions, // and then creates an instance called 'c' class C { public: static int Static(char c); int Member(char c); } c; // C // This defines 'p', a pointer to 'C' and assigns the address of 'c' to it C *p = amp;c; // This assigns a pointer-to-'Static' to 'fn'. // Since there is no 'this', 'Fn' is the correct type; and 'fn' can be used as above. fn = amp;C::Static; // This defines 'm', a pointer-to-member-of-'C' with type 'Fn', // and assigns the address of 'C::Member' to it. // You can read it right-to-left like all pointers: // "'m' is a pointer to member of class 'C' of type 'Fn'" Fn C::*m = amp;C::Member; // This uses 'm' to call 'Member' in 'c', assigning the result to 'cA' int cA = (c.*m)('A'); // This uses 'm' to call 'Member' in 'p', assigning the result to 'pA' int pA = (p-gt;*m)('A'); // This defines 'Ref', a function that accepts a reference-to-'C', // a pointer-to-member-of-'C' of type 'Fn', and a 'char', // calls the function and returns the result int Ref(C amp;r, Fn C::*m, char c) { return (r.*m)(c); } // Ref(r, m, c) // This defines 'Ptr', a function that accepts a pointer-to-'C', // a pointer-to-member-of-'C' of type 'Fn', and a 'char', // calls the function and returns the result int Ptr(C *p, Fn C::*m, char c) { return (p-gt;*m)(c); } // Ptr(p, m, c) // LEGACY: Note that to maintain existing code bases, the above definition style can still be used first; // then the original type can be defined in terms of it using the new style. // This defines 'FnC', a type of pointer-to-member-of-class-'C' of type 'Fn' typedef Fn C::*FnC; // 'FnC' can be used wherever 'Fn C::*' can FnC fnC = amp;C::Member; int RefP(C amp;p, FnC m, char c);
Смотрите также
Рекомендации
Внешние ссылки
Последняя правка сделана 2023-04-04 11:40:46
Содержание доступно по лицензии CC BY-SA 3.0 (если не указано иное).
Обратная связь: support@alphapedia.ru
Соглашение
О проекте