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

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

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

Указатели функций поддерживаются третье поколение языки программирования (Такие как PL / I, КОБОЛ, Фортран,[1] dBASE dBL и C ) и объектно-ориентированного программирования языки (такие как C ++ и D ).[2]

Указатели на простые функции

Самая простая реализация указателя функции (или подпрограммы) - это Переменная содержащий адрес функции в исполняемой памяти. Старшая языки третьего поколения Такие как PL / I и КОБОЛ, а также более современные языки, такие как Паскаль и C обычно реализуют указатели на функции таким образом.[3]

Пример на C

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

  • func1 принимает один параметр двойной точности (double) и возвращает другой параметр double и назначается функции, преобразующей сантиметры в дюймы.
  • func2 принимает указатель на массив константных символов, а также целое число и возвращает указатель на символ, и присваивается Обработка строки C функция, которая возвращает указатель на первое вхождение данного символа в массив символов.
#включают  / * для printf * /#включают  / * для strchr * /двойной cm_to_inches(двойной см) {	возвращаться см / 2.54;}// "strchr" является частью обработки строки C (т. е. не требует объявления)// См. Https://en.wikipedia.org/wiki/C_string_handling#Functionsint главный(пустота) {	двойной (*func1)(двойной) = cm_to_inches;	char * (*func2)(const char *, int) = strchr;	printf("% f% s", func1(15.0), func2(«Википедия», 'п'));	/ * выводит "5.905512 pedia" * /	возвращаться 0;}

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

 1 #включают <math.h> 2 #включают <stdio.h> 3  4 // Функция принимает указатель на функцию в качестве аргумента 5 двойной compute_sum(двойной (*funcp)(двойной), двойной вот, двойной Здравствуй) { 6     двойной сумма = 0.0; 7  8     // Добавляем значения, возвращаемые указанной функцией '* funcp' 9     int я;10     за (я = 0; я <= 100; я++) {11         // Используйте указатель функции 'funcp' для вызова функции12         двойной Икс = я / 100.0 * (Здравствуй - вот) + вот;13         двойной у = funcp(Икс);14         сумма += у;15     }16     возвращаться сумма / 101.0 * (Здравствуй - вот);17 }18 19 двойной квадрат(двойной Икс) {20      возвращаться Икс * Икс;21 }22 23 int главный(пустота) {24     двойной  сумма;25 26     // Использовать стандартную библиотечную функцию sin () как указанную функцию27     сумма = compute_sum(грех, 0.0, 1.0);28     printf("сумма (грех):% г п", сумма);29 30     // Использовать стандартную библиотечную функцию cos () как указанную функцию31     сумма = compute_sum(потому что, 0.0, 1.0);32     printf("сумма (соз):% г п", сумма);33 34     // Использование пользовательской функции square () в качестве функции, на которую указывает35     сумма = compute_sum(квадрат, 0.0, 1.0);36     printf("сумма (квадрат):% г п", сумма);37 38     возвращаться 0;39 }

Функторы

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

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

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

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

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

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

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

В C ++

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

#включают <iostream>#включают <functional>статический двойной производная(const стандартное::функция<двойной(двойной)> &ж, двойной x0, двойной eps) {    двойной eps2 = eps / 2;    двойной вот = x0 - eps2;    двойной Здравствуй = x0 + eps2;    возвращаться (ж(Здравствуй) - ж(вот)) / eps;}статический двойной ж(двойной Икс) {    возвращаться Икс * Икс;}int главный() {    двойной Икс = 1;    стандартное::cout << "d / dx (x ^ 2) [@ x =" << Икс << "] = " << производная(ж, Икс, 1e-5) << стандартное::конец;    возвращаться 0;}

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

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

#включают <iostream>с помощью пространство имен стандартное;учебный класс Фу {общественный:    int Добавить(int я, int j) {        возвращаться я+j;    }    int мульт(int я, int j) {        возвращаться я*j;    }    статический int отрицать(int я) {        возвращаться -я;    }};int bar1(int я, int j, Фу* pFoo, int(Фу::*pfn)(int,int)) {    возвращаться (pFoo->*pfn)(я,j);}typedef int(Фу::*Foo_pfn)(int,int);int bar2(int я, int j, Фу* pFoo, Foo_pfn pfn) {    возвращаться (pFoo->*pfn)(я,j);}typedef int(*PFN)(int);int bar3(int я, PFN pfn) {    возвращаться pfn(я);}int главный() {    Фу фу;    cout << "Foo :: add (2,4) =" << bar1(2,4, &фу, &Фу::Добавить) << конец;    cout << "Foo :: mult (3,5) =" << bar2(3,5, &фу, &Фу::мульт) << конец;    cout << "Foo :: negate (6) =" << bar3(6, &Фу::отрицать) << конец;    возвращаться 0;}

Альтернативный синтаксис C и C ++

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

C и C ++

// Объявляется 'F', функция, которая принимает 'char' и возвращает 'int'. Определение в другом месте.int F(char c);// Это определяет 'Fn', тип функции, которая принимает 'char' и возвращает 'int'.typedef int Fn(char c);// Это определяет 'fn', переменную типа указатель на'Fn ', и присваивает ей адрес' F '.Fn *fn = &F;      // Обратите внимание, что '&' не требуется, но оно указывает на то, что делается.// Это вызывает 'F' с помощью 'fn', присваивая результат переменной 'a'int а = fn('А');// Это определяет 'Call', функцию, которая принимает указатель-на-'Fn ', вызывает его и возвращает результатint Вызов(Fn *fn, char c) {   возвращаться fn(c);} // Вызов (fn, c)// Это вызывает функцию 'Call', передавая 'F' и присваивая результат 'call'int вызов = Вызов(&F, 'А');   // Опять же, '&' не требуется// НАСЛЕДИЕ: обратите внимание, что для поддержки существующей кодовой базы сначала можно использовать вышеупомянутый стиль определения;// тогда исходный тип можно определить в терминах этого нового стиля.// Это определяет 'PFn', тип указателя на тип-Fn.typedef Fn *PFn;// 'PFn' можно использовать везде, где можно 'Fn *'PFn pfn = F;int CallP(PFn fn, char c);

C ++

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

// Это определяет 'C', класс с похожими статическими функциями и функциями-членами,// а затем создает экземпляр с именем 'c'учебный класс C {общественный:статический int Статический(char c);int Член(char c);} c; // C// Это определяет 'p', указатель на 'C' и присваивает ему адрес 'c'C *п = &c;// Это назначает указатель на'Static 'функции' fn '.// Так как здесь нет this, правильный тип Fn; и "fn" можно использовать, как указано выше.fn = &C::Статический;// Это определяет 'm', указатель на член-член 'C' с типом 'Fn',// и присваивает ему адрес 'C :: Member'.// Вы можете читать его справа налево, как и все указатели:// "'m' - указатель на член класса 'C' типа 'Fn'"Fn C::*м = &C::Член;// Это использует 'm' для вызова 'Member' в 'c', присваивая результат 'cA'int cA = (c.*м)('А');// Это использует 'm' для вызова 'Member' в 'p', присваивая результат 'pA'int pA = (п->*м)('А');// Это определяет 'Ref', функцию, которая принимает ссылку на 'C',// указатель на член класса 'C' типа 'Fn' и 'char',// вызывает функцию и возвращает результатint Ссылка(C &р, Fn C::*м, char c) {   возвращаться (р.*м)(c);} // Ссылка (r, m, c)// Это определяет 'Ptr', функцию, которая принимает указатель на 'C',// указатель на член класса 'C' типа 'Fn' и 'char',// вызывает функцию и возвращает результатint Ptr(C *п, Fn C::*м, char c) {   возвращаться (п->*м)(c);} // Ptr (p, m, c)// НАСЛЕДИЕ: обратите внимание, что для поддержки существующей кодовой базы сначала можно использовать вышеупомянутый стиль определения;// тогда исходный тип можно определить в терминах этого нового стиля.// Это определяет 'FnC', тип указателя на член-класс-'C 'типа' Fn 'typedef Fn C::*FnC;// 'FnC' можно использовать везде, где можно 'Fn C :: *'FnC fnC = &C::Член;int RefP(C &п, FnC м, char c);

Смотрите также

Рекомендации

  1. ^ Эндрю Дж. Миллер. "Примеры Фортрана". http://www.esm.psu.edu/~ajm138/fortranexamples.html. Получено 2013-09-14.CS1 maint: location (связь)
  2. ^ "Учебники по указателям на функции". http://www.newty.de/: логотип. Получено 2011-04-13. Указатели функций - это указатели, то есть переменные, которые указывают на адрес функции.
  3. ^ "Учебники по указателям на функции". http://www.newty.de/: логотип. Получено 2011-04-13. Важное примечание: указатель функции всегда указывает на функцию с определенной сигнатурой! Таким образом, все функции, которые вы хотите использовать с одним и тем же указателем на функцию, должны иметь одинаковые параметры и возвращаемый тип!
  4. ^ «Опыт: промежуточный язык: C ++: используйте функтор для обратных вызовов в C ++». http://www.devx.com/: DevX.com. 2005-01-31. Получено 2011-04-13. Если вы хотите использовать функцию-член в качестве функции обратного вызова, тогда эта функция-член должна быть связана с объектом класса, прежде чем ее можно будет вызвать. В этом случае вы можете использовать функтор [с примером на этой странице].

внешняя ссылка