Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C++ для начинающих (Стенли Липпман) 3-е хххх.pdf
Скачиваний:
86
Добавлен:
30.05.2015
Размер:
5.92 Mб
Скачать

С++ для начинающих

359

prog - d dataO

Модифицируйте программу так, чтобы она распознавала подобную ошибку и сообщала о ней.

Упражнение 7.18

В нашей программе не предусмотрен случай, когда опции -l или -o задаются несколько раз. Реализуйте такую возможность. Какова должна быть стратегия при разрешении конфликта?

Упражнение 7.19

В нашей реализации задание неизвестной опции приводит к фатальной ошибке. Как вы думаете, это оправдано? Предложите другое поведение.

Упражнение 7.20

Добавьте поддержку опций, начинающихся со знака плюс (+), обеспечив обработку +s и +pt, а также +sp и +ps. Предположим, что +s включает строгую проверку синтаксиса, а +p допускает использование устаревших конструкций. Например:

prog +s +p -d -b 1024 dataO

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

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

sort( start, end, compare );

где start и end являются указателями на элементы массива строк. Функция sort() сортирует элементы между start и end, а аргумент compare задает операцию сравнения двух строк этого массива.

Какую реализацию выбрать для compare? Мы можем сортировать строки лексикографически, т.е. в том порядке, в котором слова располагаются в словаре, или по длине более короткие идут раньше более длинных. Нам нужен механизм для задания альтернативных операций сравнения.

(Заметим, что в главе 12 описан алгоритм sort() и другие обобщенные алгоритмы из стандартной библиотеки С++. В этом разделе мы покажем свою собственную версию sort() как пример употребления указателей на функции. Наша функция будет упрощенным вариантом стандартного алгоритма.)

Один из способов удовлетворить наши потребности использовать в качестве третьего аргумента compare указатель на функцию, применяемую для сравнения.

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

С++ для начинающих

360

7.9.1. Тип указателя на функцию

Как объявить указатель на функцию? Как выглядит формальный параметр, когда фактическим аргументом является такой указатель? Вот определение функции

#include <string>

int lexicoCompare( const string &sl, const string &s2 ) { return sl.compare(s2);

lexicoCompare(), которая сравнивает две строки лексикографически:

}

Если все символы строк s1 и s2 равны, lexicoCompare() вернет 0, в противном случае отрицательное число, если s1 меньше чем s2, и положительное, если s1 больше s2.

Имя функции не входит в ее сигнатуру она определяется только типом возвращаемого значения и списком параметров. Указатель на lexicoCompare() должен адресовать

int *pf( const string &, const string & ) ;

функцию с той же сигнатурой. Попробуем написать так:

// нет, не совсем так

Эта инструкция почти правильна. Проблема в том, что компилятор интерпретирует ее как объявление функции с именем pf, которая возвращает указатель типа int*. Список параметров правилен, но тип возвращаемого значения не тот. Оператор разыменования (*) ассоциируется с данным типом (int в нашем случае), а не с pf. Чтобы исправить

int (*pf)( const string &, const string & ) ;

положение, нужно использовать скобки:

// правильно

pf объявлен как указатель на функцию с двумя параметрами, возвращающую значение типа int, т.е. такую, как lexicoCompare().

pf способен адресовать и приведенную ниже функцию, поскольку ее сигнатура совпадает с типом lexicoCompare():

int sizeCompare( const string &sl, const string &s2 );

int calc( int , int );

Функции calc() и gcd()другого типа, поэтому pf не может указывать на них: int gcd( int , int );

Указатель, который адресует эти две функции, определяется так:

С++ для начинающих

361

int (*pfi)( int, int );

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

int printf( const char*, ... ); int strlen( const char* );

int (*pfce)( const char*, ... ); // может указывать на printf()

считается, что функции различны. Таковы же и типы указателей.

int (*pfc)( const char* );

// может указывать на strlen()

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

7.9.2. Инициализация и присваивание

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

lexicoCompare;

получается указатель типа

int (*)( const string &, const string & );

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

int (*pfi)( const string &, const string & ) = lexicoCompare;

инициализируется следующим образом:

int (*pfi2)( const string &, const string & ) = &lexicoCompare;

pfi = lexicoCompare;

Ему можно присвоить значение: pfi2 = pfi;

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

С++ для начинающих

362

int calc( int, int );

int (*pfi2s)( const string &, const string & ) = 0; int (*pfi2i)( int, int ) = 0;

int main() {

pfi2i = calc; // правильно

pri2s = calc; // ошибка: несовпадение типов pfi2s = pfi2i; // ошибка: несовпадение типов return 0;

}

Такой указатель можно инициализировать нулем или присвоить ему нулевое значение, в этом случае он не адресует функцию.

7.9.3. Вызов

Указатель на функцию применяется для вызова функции, которую он адресует. Включать оператор разыменования при этом необязательно. И прямой вызов функции по имени, и

#include <iostream>

int min( int*, int );

int (*pf)( int*, int ) = min;

const int iaSize = 5;

int ia[ iaSize ] = { 7, 4, 9, 2, 5 };

int main() {

cout << "Прямой вызов: min: "

<< min( ia, iaSize ) << endl;

cout << "Косвенный вызов: min: " << pf( ia, iaSize ) << endl;

return 0;

}

int min( int* ia, int sz ) { int minVal = ia[ 0 ];

for ( int ix = 1; ix < sz; ++ix ) if ( minVal > ia[ ix ] )

minVal = ia[ ix ]; return minVal;

косвенный вызов по указателю записываются одинаково:

}

Вызов

pf( ia, iaSize );

может быть записан также и с использованием явного синтаксиса указателя:

(*pf)( ia, iaSize );