Четверг, 09.01.2025, 11:07
Главная Регистрация RSS поиск
Приветствую Вас, Гость
Меню сайта
Категории раздела
HTML [44]
Visual C++ и MFC [21]
c++ [78]
php [19]
Javascript [15]
C# [51]
загрузки [0]
XNA [10]
создание игр с помощью xna
Наш опрос
Каким языком программирования вы увлекаетесь
Всего ответов: 2420
Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0
Реклама
Главная » Статьи » c++

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

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

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

тип функции (*имя_указателя)(спецификация_параметров);

Например: int(*func1ptr) (char); — определение указателя func1ptr на функцию с параметром типа char, возвращающую значение типа int.

Если приведенную синтаксическую конструкцию записать без первых круглых скобок, то есть в виде int *fun(char); то компилятор воспримет ее как прототип некой функции с именем fun и параметром типа char, возвращающей значение указателя типа int *.

Второй пример: char * (*func2Ptr) (char *, int); - определение указателя func2Ptr на функцию с параметрами типа указатель на char и типа int, возвращающую значение типа указатель на char.

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

Пример №1

/*Пример определения и использования 
указателей на функции
*/ 

#include<iostream.h>

// описание функции f1
void f1(void) 
{
 cout << "\n Выполняется f1()";
}

// описание функции f2
void f2(void)
{
 cout << "\n Выполняется f2()";
}

void main()
{
 // объявление указателя на функцию, 
 void (*ptr)(void); // ptr - указатель на функцию
 ptr = f2; // ptr инициализируется адресом f2()
 (*ptr)(); // вызов f2() по ее адресу
 ptr = f1; // присваивается адрес f1()
 (*ptr)(); // вызов f1() по ее адресу
 ptr(); // вызов эквивалентен (*ptr)();
}

Результат выполнения программы:

Выполняется f2()
Выполняется f1()
Выполняется f1()

В программе описан указатель ptr на функцию, и ему последовательно присваиваются адреса функции f2 и fl. Обратите внимание на форму вызова функции с помощью указателя на функцию:

(*имя_укаэателя)(список_фактических_параметров);

Значением имени_указателя служит адрес функции, а с помощью операции разыменования * обеспечивается обращение по адресу к этой функции. Однако будет ошибкой записать вызов функции без скобок в виде *ptr(); Дело в том, что операция () имеет более высокий приоритет, нежели операция обращения по адресу *. Следовательно, в соответствии с синтаксисом будет вначале сделана попытка обратиться к функции ptr(). И уже к результату будет отнесена операция разыменования, что будет воспринято как синтаксическая ошибка.

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

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

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

Пример №2

/*Вызов функций по адресам через указатель
*/
#include<iostream.h>

// описания функций 
// функции одного типа с одинаковыми сигнатурами
int add (int n, int m) { return n + m;}
int div (int n, int m) { return n % m;}
int mult (int n, int m) { return n * m;}
int subt (int n, int m) { return n - m;}

void main()
{
 int (*par)(int, int); // указательна функцию
 int a = 6, b = 2; /* задаются начальные значения 
 для переменных a и b */
 char c = '+';
 while(c != ' ')
 {
 cout << "\nАргументы: а = " << a 
 << ", b = " << b;
 cout << "\n Результат для с = \'" << c << "\'" 
 << " равен ";

 switch(c)
 {
 case '+': 
 par = add; // par получает адрес функции add
 c = '%'; break;
 case '-': 
 par = subt; // par получает адрес функции subt
 c = ' '; break;
 case '*': 
 par = mult; // par получает адрес функции mult
 c = '-'; break;
 case '%': 
 par = div; // par получает адрес функции div
 c = '*'; break;
 }
 cout << (*par)(a,b); /* вызов функции по адресу, 
 результат, возвращаемый функцией, выводится на экран */
 }
 cout << endl;
}

Результат выполнения программы

Аргументы: a = 6, b = 2. Результат для с = '+' равен 8
Аргументы: a = 6, b = 2. Результат для с = '%' равен 0
Аргументы: a = 6, b = 2. Результат для с = '*' равен 12
Аргументы: a = 6, b = 2. Результат для с = '-' равен 4

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

Указатели на функции могут быть объединены в массивы. Например, float (*ptrArray) (char) [4] ; - описание массива с именем ptrArray из четырех указателей на функции, каждая из которых имеет параметр типа char и возвращает значение типа float. Чтобы обратиться, например, к третьей из этих функций, потребуется такой оператор:

float a = (*ptrArray[2]) ('f');

Как обычно, индексация массива начинается с 0, и поэтому третий элемент массива имеет индекс 2.

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

Пример №3

/*пример с массивом указателей на функции
*/
#include<iostream.h>

// объявление функций
void func1(int);
void func2(int);
void func3(int);

void main()
{
/* объявление массива, в котором хранятся указатели на 
функции func1, func2 и func3*/
 void (*f[3])(int) = {func1, func2, func3};
 int choice;

 cout << "Введите число между 1 и 3,"
 " другое число - окончание: ";
 cin >> choice;

 while(choice >= 1 && choice < 4)
 {
 (*f[choice-1])(choice); // вызов функции f[choice]
 cout << "Введите число между 1 и 3,"
 " другое число - окончание: ";
 cin >> choice;
 }
 cout << "Вы ввели " << choice << " для окончания\n"; 
}

// описания функций
void func1(int a)
{
 cout << "\nВы ввели " << a 
 << ", поэтому была вызвана func1\n";
}

void func2(int b)
{
 cout << "\nВы ввели " << b 
 << ", поэтому была вызвана func2\n";
}

void func3(int a)
{
 cout << "\nВы ввели " << a 
 << ", поэтому была вызвана func3\n";
}

Результаты выполнения программы

Введите число между 1 и 3, другое число - окончание: 1
Вы ввели 1, поэтому была вызвана func1

Введите число между 1 и 3, другое число - окончание: 2
Вы ввели 2, поэтому была вызвана func2

Введите число между 1 и 3, другое число - окончание: 3
Вы ввели 3, поэтому была вызвана func3

Введите число между 1 и 3, другое число - окончание: 10
Вы ввели 10 для окончания

В программе определены три функции — func1, func2, func3 - каждая из которых принимает целый аргумент и ничего не возвращает. Указатели на эти три функции хранятся в массиве f, который объявлен следующим образом:

void (*f[3])(int) = {func1, func2, func3};
Массив получает в качестве начальных значений имена 3-х функций. Когда пользователь вводит значения 1, 2 или 3, это значение (минус 1) используется в качестве индекса в массиве указателей на функции.

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

(*f[choice-1])(choice);

В этом вызове (*f[choice-1]) определяет указатель, расположенный в элементе массива с индексом choice-1. Указатель разыменовывается, чтобы вызвать функцию, и choice передается функции как аргумент. Каждая функция печатает имя функции, чтобы показать, что вызов верен. 

Категория: c++ | Добавил: slava (30.05.2011)
Просмотров: 10531 | Рейтинг: 4.9/7
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]