Введение в язык Си++

         

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


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

void error(char* p) { /* ... */ }

void (*efct)(char*); // указатель на функцию

void f() { efct = error // efct указывает на error (*efct)("error"); // вызов error через efct }

Чтобы вызвать функцию через указатель, например, efct, надо сначала этот указатель разыменовать, *efct. Поскольку операция вызова функции () имеет более высокий приоритет, чем операция разыменования *, то нельзя писать просто *efct("error"). Это означает *efct("error"), а это ошибка в типе. То же относится и к синтаксису описаний (см. также #7.3.4).

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

void (*pf)(char*); // указатель на void(char*) void f1(char*); // void(char*) int f2(char*); // int(char*) void f3(int*); // void(int*)

void f() { pf = f1 // ok pf = f2 // ошибка: не подходит возвращаемый тип pf = f3 // ошибка: не подходит тип параметра

(*pf)("asdf"); // ok (*pf)(1); // ошибка: не подходит тип параметра

int i = (*pf)("qwer"); // ошибка: void присваивается int'у }

Правила передачи параметров для непосредственных вызовов функции и для вызовов функции через указатель одни и те же.

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

typedef int (*SIG_TYP)(); // из typedef void (*SIG_ARG_TYP); SIG_TYP signal(int,SIG_ARG_TYP);

Бывает часто полезен вектор указателей на функцию. Например, система меню для моего редактора с мышью*4 реализована с помощью векторов указателей на функции для представления действий. Подробно эту систему здесь описать не получится, но вот общая идея:

typedef void (*PF)();

PF edit_ops[] = { // операции редактирования cut, paste, snarf, search };




PF file_ops[] = { // управление файлом open, reshape, close, write };

Затем определяем и инициализируем указатели, определяющие действия, выбранные в меню, которое связано с кнопками (button) мыши:

PF* button2 = edit_ops; PF* button3 = file_ops;

В полной реализации для определения каждого пункта меню требуется больше информации. Например, где-то должна храниться строка, задающая текст, который высвечивается. При использовании системы значение кнопок мыши часто меняется в зависимости от ситуации. Эти изменения осуществляются (частично) посредством смены значений указателей кнопок. Когда пользователь выбирает пункт меню, например пункт 3 для кнопки 2, выполняется связанное с ним действие:

(button2[3])();

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

Указатели на функции можно использовать для задания полиморфных подпрограмм, то есть подпрограмм, которые могут применяться к объектам многих различных типов:

typedef int (*CFT)(char*,char*);

int sort(char* base, unsigned n, int sz, CFT cmp) /* Сортирует "n" элементов вектора "base" в возрастающем порядке с помощью функции сравнения, указываемой "cmp". Размер элементов "sz".

Очень неэффективный алгоритм: пузырьковая сортировка */ { for (int i=0; iname, Puser(q)-name); }

int cmp2(char*p, char* q) // Сравнивает числа dept { return Puser(p)-dept-Puser(q)-dept; }

Эта программа сортирует и печатает:

main () { sort((char*)heads,6,sizeof(user),cmp1); print_id(heads,6); // в алфавитном порядке cout

Можно взять адрес inline-функции, как, впрочем, и адрес перегруженной функции(#с.8.9).


Содержание раздела