
- •Оглавление
- •Введение
- •1. Рекомендации по изучению теоретического материала
- •1.1. Общие указания
- •1.2. Рекомендации по конкретным разделам курса
- •1.3. Методология обобщенного программирования
- •Почему не интерфейсы?
- •Вызов объекта
- •Реализация компараторов
- •Техника traits
- •2. Рекомендации по подготовке эссе, рефератов и докладов
- •2.1. Эссе: рекомендации по подготовке
- •2.2. Рефераты: рекомендации по подготовке
- •2.3. Доклады: рекомендации по подготовке
- •2.4. Моделирующие программы: рекомендации по разработке
- •3.2. Домашнее задание №2. Использование алгоритмов и контейнеров данных в прикладной задаче. Задачи домашнего задания
- •Задание
- •Требования к отчетности
- •Предлагаемые этапы выполнения задания
- •Теоретический материал, необходимый для выполнения домашнего задания
- •Функциональный и объектно-ориентированный подходы к программированию – краткое описание
- •Основы uml
- •Понятие агрегации в объектно-ориентированном программировании
- •Рекомендованные правила оформления исходных текстов
- •4.1. Правила выбора идентификаторов
- •4.2. Выравнивание исходных текстов Символ табуляции запрещён
- •Выравнивание блоков
- •Пробелы
- •Длинные операторы
- •4.3. Комментарии
- •Заключение
- •Литература
- •Кафедра компьютерной фотоники
Вызов объекта
Часто мы хотим, чтобы для какой-то задачи можно было применить и функцию, и объект заранее заданного класса. Например, в алгоритме STL std::for_each вызывается одна и та же функция для всех объектов контейнера. Мы хотим иметь возможность:
Вызвать для каждого элемента контейнера функцию с одним параметром, передав в качестве параметра элемент (самый простой вариант).
Вызвать для каждого элемента контейнера функцию с несколькими параметрами, передав в качестве одного из них элемент, а в качестве других – фиксированные значения.
Вызвать для каждого элемента контейнера метод его класса (возможно, с передачей дополнительных параметров). Хочется иметь возможность применить этот метод и для контейнера указателей, и для контейнера объектов.
Вызвать для каждого элемента контейнера метод объекта определенного класса (возможно, с передачей дополнительных параметров).
Это действительно возможно сделать. Дело в том, что шаблон std::for_each выглядит примерно так:
template <class IterType,
class FunctionType> inline
FunctionType for_each(IterType _First, IterType _Last, FunctionType _Func)
{
for ( IterType iter = _First ; iter!= _Last ; ++iter)
_Func( *iter );
return (_Func);
}
И в роли _Func может быть объект любого типа FunctionType – лишь бы этот объект можно было скопировать внутрь функции и вызвать, передав ему в качестве параметра *iter.
Соответственно, _Func может быть как указателем на функцию, принимающую тип элемента контейнера, так и объектом класса, имеющего метод:
void operator()( ElementType& )
Этого достаточно. Например, если хочется вызвать функцию с несколькими параметрами, мы можем написать код:
class Printer
{
public:
Printer( std::istream& stream )
:Stream(stream)
{
}
void operator()(int a )
{
Print( Stream , a );
}
private:
std::istream& Stream;
};
Printer printer( stream1 );
std::for_each( int_vector.begin() , int_vector.end() , printer );
Для каждого элемента контейнера будет вызван operator() класса Printer и, как следствие, функция Print с двумя параметрами – в качестве первого будет передан stream1, в качестве второго – элемент контейнера.
Реализация компараторов
Техника вызова объекта позволяет нам реализовать специальные объекты, отвечающие, например, за сравнение элементов контейнера. Часто мы хотим отделить знание о способе сравнения от типа элемента. Для этого нам достаточно передавать контейнеру специальный объект – компаратор. Контейнер при необходимости сравнить элементы будет вызывать метод operator() объекта-компаратора (или функцию сравнения). Метод (или функция) должны принимать два объекта заданного типа и возвращать bool (результат сравнения). Подробнее см. [2, разд. 4.3].
Техника traits
Некоторые сложные контейнеры (например, строки) предъявляют большие требования к своим элементам. Для символов требуется и возможность сравнения, и наличие спецсимволов для конца строки и конца файла, и многое другое.
В то же время хочется иметь возможность применять эти контейнеры для разных типов символов (например, Unicode строк). Знание об алгоритмах сравнения символов, символе конца строки, символе конца файла и других необходимо отделить от конкретного типа символов (чтобы мы могли использовать в контейнере даже тот тип символа, автор которого об этой возможности не думал).
Для этого создается специальный тип, определяющий для другого типа некоторый набор применяемых к нему операций. Этот тип похож на тип компаратора, но отвечает не только за сравнение, но и за многое другое. Он обеспечивает все операции, необходимые другому типу (например, для того, чтобы быть элементом сложного контейнера).
Например, тип char_traits обеспечивает для другого типа все операции, необходимые для того, чтобы быть символом (элементом строки и значением, читаемым из потока). Мы можем определить для любого типа элемента тип traits с тем же набором методов, что и у char_traits, и использовать этот тип элемента в роли символа.