Jump to content

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

(Перенаправлено из указателей функций )

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

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

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

Простые указатели функций [ править ]

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

Пример на C [ править ]

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

  • func1 принимает один параметр двойной точности (double) и возвращает другой двойной точности, который присваивается функции, которая преобразует сантиметры в дюймы.
  • func2 принимает указатель на постоянный массив символов, а также целое число, и возвращает указатель на символ, а также назначается функции обработки строк C , которая возвращает указатель на первое вхождение данного символа в массив символов.
#include <stdio.h>  /* for printf */
#include <string.h> /* 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/wiki/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 <math.h>
#include <stdio.h>

// 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 <= 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;
}

Актеры [ править ]

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

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

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

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

Указатели методов [ править ]

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

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

В C++ [ править ]

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

#include <iostream>
#include <functional>
using namespace std;

static double derivative(const function<double(double)> &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;
    cout << "d/dx(x ^ 2) [@ x = " << x << "] = " << derivative(f, x, 1e-5) << endl;
    return 0;
}

Указатели на функции-члены в C++ [ править ]

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

#include <iostream>
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->*pfn)(i,j);
}

typedef int(Foo::*Foo_pfn)(int,int);

int bar2(int i, int j, Foo* pFoo, Foo_pfn pfn) {
    return (pFoo->*pfn)(i,j);
}

typedef auto(*PFN)(int) -> int;
// C++ only, same as: typedef int(*PFN)(int);

int bar3(int i, PFN pfn) {
    return pfn(i);
}

int main() {
    Foo foo;
    cout << "Foo::add(2,4) = " << bar1(2,4, &foo, &Foo::add) << endl;
    cout << "Foo::mult(3,5) = " << bar2(3,5, &foo, &Foo::mult) << endl;
    cout << "Foo::negate(6) = " << bar3(6, &Foo::negate) << 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 = &F;      // Note '&' 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(&F, 'A');   // Again, '&' 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);

С++ [ править ]

В этих примерах используются приведенные выше определения. В частности, отметим, что приведенное выше определение для 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 = &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 = &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 = &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->*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 &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->*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 = &C::Member;
int RefP(C &p, FnC m, char c);

См. также [ править ]

Ссылки [ править ]

  1. ^ Эндрю Дж. Миллер. «Примеры Фортрана» . Проверено 14 сентября 2013 г.
  2. ^ «Учебные пособия по указателям функций» . логотип. Архивировано из оригинала 16 мая 2011 г. Проверено 13 апреля 2011 г. Указатели функций — это указатели, то есть переменные, которые указывают на адрес функции.
  3. ^ «Учебные пособия по указателям функций» . логотип. Архивировано из оригинала 16 мая 2011 г. Проверено 13 апреля 2011 г. Важное примечание: указатель функции всегда указывает на функцию с определенной сигнатурой! Таким образом, все функции, которые вы хотите использовать с одним и тем же указателем функции, должны иметь одинаковые параметры и тип возвращаемого значения!
  4. ^ «Экспертиза: средний уровень языка: C++: использование функтора для обратных вызовов в C++» . DevX.com. 31 января 2005 г. Проверено 13 апреля 2011 г. Если вы хотите использовать функцию-член в качестве функции обратного вызова, то функция-член должна быть связана с объектом класса, прежде чем ее можно будет вызвать. В этом случае вы можете использовать функтор [с примером на этой странице].

Внешние ссылки [ править ]

Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: e29a34b7f42ecd8c012e20465a78ad3e__1717446120
URL1:https://arc.ask3.ru/arc/aa/e2/3e/e29a34b7f42ecd8c012e20465a78ad3e.html
Заголовок, (Title) документа по адресу, URL1:
Function pointer - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)