Бьерн Страуструп.
Язык программирования С++
116
функциях. При присваивании указателю на функцию требуется точное соответствие типа функции и
типа присваиваемого значения. Например:
void (*pf)(char*);
// указатель на void(char*)
void f1(char*);
// void(char*);
int f2(char*);
// int(char*);
void f3(int*);
// void(int*);
void f()
{
pf = &f1;
// нормально
pf = &f2;
// ошибка: не тот тип возвращаемого
// значения
pf = &f3;
// ошибка: не тот тип параметра
(*pf)("asdf");
// нормально
(*pf)(1);
// ошибка: не тот тип параметра
int i = (*pf)("qwer");
// ошибка: void присваивается int
}
Правила передачи параметров одинаковы и для обычного вызова, и для вызова с помощью указателя.
Часто бывает удобнее обозначить тип указателя на
функцию именем, чем все время использовать
достаточно сложную запись. Например:
typedef int (*SIG_TYP)(int); //
из
typedef void (SIG_ARG_TYP)(int);
SIG_TYP signal(int, SIG_ARG_TYP);
Также часто бывает полезен массив указателей на функции. Например, можно реализовать систему
меню для редактора с вводом, управляемым мышью, используя массив указателей на функции,
реализующие команды. Здесь нет возможности подробно описать такой редактор, но дадим самый
общий его набросок:
typedef void (*PF)();
PF edit
_ops[] = { // команды редактора
&cut, &paste, &snarf, &search
};
PF file_ops[] = { // управление файлом
&open, &reshape, &close, &write
};
Далее надо определить и инициализировать указатели, с помощью которых будут запускаться функции,
реализующие выбранные из меню команды. Выбор происходит нажатием клавиши мыши:
PF* button2 = edit_ops;
PF* button3 = file_ops;
Для настоящей программы редактора надо определить большее число объектов, чтобы описать каждую
позицию в меню. Например, необходимо где-то хранить строку, задающую текст, который будет
выдаваться для каждой позиции. При работе с системой меню назначение клавиш мыши будет постоянно
меняться. Частично эти изменения можно представить как изменения значений указателя, связанного с
данной клавишей. Если пользователь выбрал позицию меню, которая определяется, например, как позиция 3
для клавиши 2, то соответствующая команда реализуется вызовом:
(*button2[3])();
Чтобы полностью оценить мощность конструкции указатель на функцию, стоит попытаться написать
программу без нее. Меню можно изменять в динамике, если добавлять новые функции в таблицу
команд.
Довольно просто создавать в динамике и новые меню.
Указатели на функции помогают реализовать полиморфические подпрограммы, т.е. такие подпрограммы,
которые можно применять к объектам различных типов:
typedef int (*CFT)(void*,void*);
void sort(void* base, unsigned n, unsigned int sz, CFT cmp)
Бьерн Страуструп.
Язык программирования С++
117
/*
Сортировка вектора "base" из n элементов
в возрастающем порядке;
используется
функция сравнения, на которую указывает cmp.
Размер элементов равен "sz".
Алгоритм очень неэффективный: сортировка пузырьковым методом
*/
{
for (int i=0; i
for (int j=n-1; i
char* pj = (char*)base+j*sz; // b[j]
char* pj1 = pj - sz; // b[j-1]
if ((*cmp)(pj,pj1) < 0) {
// поменять местами b[j] и b[j-1]
for (int k = 0; k
char
temp
=
pj[k];
pj[k]
=
pj1[k];
pj1[k]
=
temp;
}
}
}
}
В подпрограмме sort неизвестен тип сортируемых объектов; известно только их число (размер
массива), размер каждого элемента и функция, которая может сравнивать объекты. Мы выбрали для
функции sort() такой же заголовок, как у qsort() - стандартной
функции сортировки из библиотеки С. Эту
функцию используют настоящие программы. Покажем, как с помощью sort() можно отсортировать
таблицу с такой структурой:
struct user {
char* name; //
имя
char* id; //
пароль
int dept; //
отдел
};
typedef user* Puser;
user heads[] = {
"Ritchie D.M.", "dmr", 11271,
"Sethi R.", "ravi", 11272,
"SZYmanski T.G.", "tgs", 11273,
"Schryer N.L.", "nls", 11274,
"Schryer N.L.", "nls", 11275
"Kernighan B.W.", "bwk", 11276
};
void print_id(Puser v, int n)
{
for (int i=0; i
cout << v[i].name << '\t'
<<
v[i].id
<<
'\t'
<<
v[i].dept
<<
'\n';
}
Чтобы иметь возможность сортировать, нужно вначале определить подходящие
функции сравнения.
Функция сравнения должна возвращать отрицательное число, если ее первый параметр меньше
второго, нуль, если они равны, и положительное число в противном случае:
int cmp1(const void* p, const void* q)
// сравнение строк, содержащих имена
{
return strcmp(Puser(p)->name, Puser(q)->name);
}
int cmp2(const void* p, const void* q)
Бьерн Страуструп.
Язык программирования С++
118
// сравнение номеров разделов
{
return Puser(p)->dept - Puser(q)->dept;
}
Следующая программа сортирует и печатает результат:
int main()
{
sort(heads,6,sizeof(user),
cmp1);
print_id(heads,6); // в алфавитном порядке
cout << "\n";
sort(heads,6,sizeof(user),cmp2);
print_id(heads,6); // по номерам отделов
}
Допустима операция взятия адреса и для
функции-подстановки, и для перегруженной функции
($$R.13.3).
Отметим, что неявное преобразование указателя на что-то в указатель типа void* не выполняется для
параметра функции, вызываемой через указатель на нее. Поэтому функцию
int cmp3(const mytype*, const mytype*);
нельзя использовать в качестве параметра для sort(). Поступив иначе, мы нарушаем заданное в
описании условие, что cmp3() должна вызываться с
параметрами типа mytype*. Если вы специально
хотите нарушить это условие, то должны использовать явное преобразование типа.
Достарыңызбен бөлісу: