Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Липпман.doc
Скачиваний:
8
Добавлен:
14.08.2019
Размер:
7.54 Mб
Скачать

12.3.6. Реализация объекта-функции

При реализации программы в разделе 12.2 нам уже приходилось определять ряд объектов-функций. В этом разделе мы изучим необходимые шаги и возможные вариации при определении класса объекта-функции. (В главе 13 определение класса рассматривается детально; в главе 15 обсуждается перегрузка операторов.)

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

// простейшая форма класса объекта-функции

class less_equal_ten {

public:

bool operator() ( int val )

{ return val <= 10; }


};

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

count_if( vec.begin(), vec.end(), less_equal_ten() );

Разумеется, возможности этого класса весьма ограничены. Попробуем применить отрицатель, чтобы подсчитать, сколько в контейнере элементов, больших 10:

count_if( vec.begin(), vec.end(),


not1(less_equal_then ()));

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

class less_equal_value {

public:

less_equal_value( int val ) : _val( val ) {}

bool operator() ( int val ) { return val <= _val; }

private:

int _val;


};

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

count_if( vec.begin(), vec.end(), less_equal_value( 25 ));

Разрешается реализовать класс и без конструктора, если параметризовать его значением, с которым производится сравнение:

template < int _val >

class less_equal_value {

public:

bool operator() ( int val ) { return val <= _val; }


};

Вот как надо было бы вызвать такой класс для подсчета числа элементов, меньших или равных 25:

count_if( vec.begin(), vec.end(), less_equal_value<25>());

(Другие примеры определения собственных объектов-функций можно найти в Приложении.)

Упражнение 12.4

Используя предопределенные объекты-функции и адаптеры, создайте объекты-функции для решения следующих задач:

  1. Найти все значения, большие или равные 1024.

  2. Найти все строки, не равные "pooh".

  3. Умножить все значения на 2.

Упражнение 12.5

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

12.4. Еще раз об итераторах

Следующая реализация шаблона функции не компилируется. Можете ли вы сказать, почему?

// в таком виде это не компилируется

template < typename type >

int

count( const vector< type > &vec, type value )

{

int count = 0;

vector< type >::iterator iter = vec.begin();

while ( iter != vec.end() )

if ( *iter == value )

++count;

return count;


}

Проблема в том, что у ссылки vec есть спецификатор const, а мы пытаемся связать с ней итератор без такого спецификатора. Если бы это было разрешено, то ничто не помешало бы нам модифицировать с помощью этого итератора элементы вектора. Для предотвращения подобной ситуации язык требует, чтобы итератор, связанный с const-вектором, был константным. Мы можем сделать это следующим образом:

// правильно: это компилируется без ошибок


vector< type>::const_iterator iter = vec.begin();

Требование, чтобы с const-контейнером был связан только константный итератор, аналогично требованию о том, чтобы const-массив адресовался только константным указателем. В обоих случаях это вызвано необходимостью гарантировать, что содержимое const-контейнера не будет изменено.

Операции begin() и end() перегружены и возвращают константный или неконстантный итератор в зависимости от наличия спецификатора const в объявлении контейнера. Если дана такая пара объявлений:

vector< int > vec0;


const vector< int > vec1;

то при обращениях к begin() и end() для vec0 будет возвращен неконстантный, а для vec1 – константный итератор:

vector< int >::iterator iter0 = vec0.begin();


vector< int >::const_iterator iter1 = vec1.begin();

Разумеется, присваивание константному итератору неконстантного разрешено всегда. Например:

// правильно: инициализация константного итератора неконстантным


vector< int >::const_iterator iter2 = vec0.begin();