C++. Бархатный путь. Часть 1 - Вызов функции

ОГЛАВЛЕНИЕ

 

Вызов функции

Для вызова функции с помощью указателя использование операции разыменования не обязательно. Полная форма вызова

char* MyPointChar = (*MyPT3)(7,7,NULL,7.7);

имеет краткую эквивалентную форму

char* MyPointChar = MyPT3(7,7,NULL,7.7);

Значением выражения MyPT3 является адрес функции.

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

char* (*MyPtArray[3]) (int, int, int*, float);
Здесь описан массив указателей из 3 элементов. Инициализация массива указателей возможна лишь после объявления
трёх однотипных функций:
extern char* MyFF1 (int, int, int*, float);
extern char* MyFF2 (int, int, int*, float);
extern char* MyFF3 (int, int, int*, float);
char* (*MyPtArray[3]) (int, int, int*, float) =
{
MyFF1,
MyFF2,
MyFF3
}; // Инициализация массива указателей.

Вызов функции (например, MyFF3()) с помощью элемента массива указателей можно осуществить следующим образом:

char* MyPointChar = MyPtArray[2](7,7,NULL,7.7);

Указатель на функцию может быть описан как параметр функции:

void MonitorF(int,int,int*,float,char*(*)(int,int,int*,float));
// Торжество абстрактного описателя!

И этому параметру можно присвоить значение (значение по умолчанию):

void MonitorF(int,int,int*,float,char*(*)(int,int,int*,float)=MyFF1);

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

А вот как может выглядеть определение функции MonitorF:

#include <assert.h> 
/*
Заголовочный файл, содержащий макроопределение assert.
Это макроопределение преобразуется в условный оператор if.
Если в ходе проверки значение условного выражения оказывается
равным нулю, то происходит прерывание выполнения программы.
*/
void MonitorF (
int val1,
int val2,
int* pVal,
float fVal,
char*(*pParF)(int,int,int*,float)
)
{
char* pChar;
assert(pVal != NULL);
assert(pParF != NULL);
//Это всего лишь проверка того, не являются ли указатели пустыми...
pChar = pParF(val1, val2, pVal, fVal);
}

Возможные варианты вызова этой функции:

int MMM;
int* pIval = &MMM;
/*
Указатель pIval используется для инициализации третьего параметра.
*/
MMM = 100;
/*
А значение объекту, на который настроен указатель pIval, может быть
изменено в любой момент.
*/
MonitorF(9,9,pIval,9.9);
/*
При вызове используем значение указателя на функцию, присвоенное последнему
параметру по умолчанию.
*/
MonitorF(11,11,pIval,11.11,MyFF3);
/* А теперь передаём адрес новой функции.*/

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

ReturnerF(int, int)

Определим теперь тип указателя на функцию, который будет возвращаться функцией ReturnerF(int, int).

char* (*)(int,int,int*,float)

Теперь остаётся правильно соединить обе части объявления.

char* (*ReturnerF(int, int))(int,int,int*,float);

Получилась такая вот матрёшка. Функция о двух целочисленных параметрах, возвращающая указатель на функцию, которая возвращает указатель на объект типа char и имеет собственный список формальных параметров вида: (int,int,int*,float). Нет предела совершенству!

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

Есть такие функции! Здесь их целых три: MyFF1, MyFF2, MyFF3.

Приступаем к реализации и параллельно обыгрываем параметры.

char* (*ReturnerF(int param1, int param2))(int,int,int*,float)
{
char* (*PointF) (int,int,int*,float);
/*
Это всего лишь указатель на функцию. Мы можем себе позволить этот пустяк.
*/
if (!param1) return NULL;
switch param2
{
case 1: PointF = MyFF1; break;
case 2: PointF = MyFF2; break;
case 3: PointF = MyFF3; break;
default: PointF = NULL; break;
}
return PointF;
}

Теперь только вызов! Наша функция возвращает адрес функции. И поэтому самое простое - это вызов функции непосредственно из точки возврата функции ReturnerF:

int val1, val2;
:::::
MyPointChar = (ReturnerF(val1,val2))(7,7,NULL,7.7);
Всё было бы хорошо, если бы только не существовала вероятность возвращения пустого указателя.
Так что придётся воспользоваться ранее объявленным указателем на функцию, проверять возвращаемое значение и 
только потом вызывать функцию по означенному указателю. Это не намного сложнее:
MyPtArray[3] = ReturnerF(val1,val2);
if (MyPtArray[3])
{MyPointChar = (MyPtArray[3])(7,7,NULL,7.7);}
/* Вот и элемент массива указателей пригодился.*/

Настало время вспомнить о typedef-спецификаторе. С его помощью запись указателя на функцию можно сделать компактнее:

typedef char* (*PPFF) (int,int,int*,float);

Здесь надо представлять всё ту же матрёшку. Замещающий идентификатор PPFF располагается внутри определяемого выражения. И вот новое объявление старой функции.

PPFF ReturnerF(int, int);

В процессе трансляции будет восстановлен исходный вид объявления.