Выбрать главу
Указатели на функции

Указатель на функцию ссылается на входную точку этой функции.

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

Указатели на функции также позволяют передавать функции в качестве аргументов другим функциям. Адрес функции можно получить, используя имя функции без круглых скобок и аргументов. (Этот процесс подобен получению адреса массива, когда также используется только его имя без индекса.) Если присвоить адрес функции указателю, то эту функцию можно вызвать через указатель. Например, рассмотрим следующую программу. Она содержит две функции, vline() и hline(), которые рисуют на экране вертикальные и горизонтальные линии заданной длины.

#include <iostream>

using namespace std;

void vline(int i), hline(int i);

int main()

{

 void (*p)(int i);

 p = vline; // указатель на функцию vline()

 (*p)(4); // вызов функции vline()

 p = hline; // указатель на функцию hline()

 (*p)(3); // вызов функции hline()

 return 0;

}

void hline(int i)

{

 for( ; i; i--) cout << "-";

 cout << "\n";

}

void vline(int i)

{

 for( ; i; i--) cout << "|\n";

}

Вот как выглядят результаты выполнения этой программы.

I

I

I

I

- - -

Рассмотрим эту программу в деталях. В первой строке тела функции main() объявляется переменная р как указатель на функцию, которая принимает один целочисленный аргумент и не возвращает никакого значения. Это объявление не определяет, какая функция имеется в виду. Оно лишь создает указатель, который можно использовать для адресации любой функции этого типа. Необходимость круглых скобок, в которые заключен указатель , следует из С++-правил предшествования.

В следующей строке указателю р присваивается адрес функции vline(). Затем выполняется вызов функции vline() с аргументом 4. После этого указателю р присваивается адрес функции hline(), и с помощью этого указателя реализуется ее вызов.

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

(*p) (4);

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

p (4);

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

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

void qsort(void * start, size_t length, size_t size, int (*compare) (const void *, const void *));

Функция qsort()это функция сортировки из стандартной С++-библиотеки.

Прототип функции qsort() "прописан" в заголовке <cstdlib>, в котором также определен тип size_t (как тип unsigned int). Чтобы использовать функцию qsort(), необходимо передать ей указатель на начало массива объектов, который вы хотите отсортировать (параметр start), длину этого массива (параметр length), размер в байтах каждого элемента (параметр size) и указатель на функцию сравнения элементов массива (параметр *compare).