Бьерн Страуструп. Язык программирования С++ Второе дополненное издание



Pdf көрінісі
бет95/256
Дата11.07.2022
өлшемі2,87 Mb.
#37591
1   ...   91   92   93   94   95   96   97   98   ...   256
Байланысты:
Бьерн Страуструп. Язык программирования С . М Бином, 2011

4.6.9 Указатель на функцию 
Возможны только две операции с функциями: вызов и взятие адреса. Указатель, полученный с 
помощью последней операции, можно впоследствии использовать для вызова функции. Например: 
void error(char* p) { /* ... */ } 
void (*efct)(char*); // указатель на функцию 
void f() 

efct = &error; // efct настроен на функцию error 
(*efct)("error"); // вызов error через указатель efct 

Для вызова функции с помощью указателя (efct в нашем примере) надо вначале применить операцию 
косвенности к указателю - *efct. Поскольку приоритет операции вызова () выше, чем приоритет 
косвенности *, нельзя писать просто *efct("error"). Это будет означать *(efct("error")), что является 
ошибкой. По той же причине скобки нужны и при описании указателя на функцию. Однако, писать 
просто efct("error") можно, т.к. транслятор понимает, что efct является указателем на функцию, и 
создает команды, делающие вызов нужной функции. 
Отметим, что формальные параметры в указателях на функцию описываются так же, как и в обычных 


Бьерн Страуструп.
Язык программирования С++ 
 
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; ifor (int j=n-1; ichar* 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; kchar 
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; icout << 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*. Если вы специально 
хотите нарушить это условие, то должны использовать явное преобразование типа. 


Достарыңызбен бөлісу:
1   ...   91   92   93   94   95   96   97   98   ...   256




©emirsaba.org 2024
әкімшілігінің қараңыз

    Басты бет