1. Темы лекции: Распределители, функторы, адаптеры, алгоритмы.
Практическое задание: Функторы, адаптеры, алгоритмы.
Тренер: Игорь Шкулипа, к.т.н.
С++ Библиотеки STL и Qt. Занятие 2
2. http://www.slideshare.net/IgorShkulipa 2
Распределители памяти
Каждый контейнер имеет распределитель памяти (allocator),
который используется при выделении памяти под элементы контейнера и
предназначен для того, чтобы освободить пользователей контейнеров, от
подробностей физической организации памяти.
Стандартная библиотека обеспечивает стандартный распределитель
памяти, заданный стандартным шаблоном allocator из заголовочного
файла <memory>, который выделяет память при помощи операции new
( ) и по умолчанию используется всеми стандартными контейнерами.
Класс allocator обеспечивает стандартные способы выделения и
перераспределения памяти, а также стандартные имена типов для
указателей и ссылок.
Пользователь может задать свои распределители памяти,
предоставляющие альтернативный доступ к памяти.
Стандартные контейнеры и алгоритмы получают память и обращаются к
ней через средства, обеспечиваемые распределителем памяти.
4. http://www.slideshare.net/IgorShkulipa 4
Функторы и предикаты
Функторы и предикаты - это классы, объекты которых похожи на
функцию (в них перегружен оператор () ):
• предикат - оператор () должен возвращать значение типа bool
• функтор - у оператора () тип возвращаемого значения должен
быть отличным от bool
Предикат:
struct cmp
{
bool operator ()(int a, int b) const
{
return a < b;
}
}
Функтор:
struct sum
{
int operator ()(int a, int b) const
{
return (a + b) * (a + b);
}
}
5. http://www.slideshare.net/IgorShkulipa 5
Функторы STL
В STL уже реализовано много полезных функторов:
• minus
• plus
• multip
• divides
• modulus
• logical_or
• logical_and
• logical_not
• less
• grater
• less_equal
• grater_equal
• not_equal
• equal_to
Пример. Сортировка вектора в обратном порядке:
vector<int> v=…;
sort(v.begin(), v.end(), grater<int>());
6. http://www.slideshare.net/IgorShkulipa 6
Strategy
Паттерн Strategy предназначенный для определения семейства
алгоритмов и инкапсуляции каждого из них и обеспечения их
взаимозаменяемости.
Переносит в отдельную иерархию классов все детали, связанные
с реализацией алгоритмов.
10. http://www.slideshare.net/IgorShkulipa 10
Использование стратегий
int main()
{
IStrategyClient* stClient=new StrategyClient1();
stClient->SetStrategy(new GoToWorkStrategy());
stClient->UseStrategy();
cout<<"n";
stClient->SetStrategy(new GoToGymStrategy());
stClient->UseStrategy();
cout<<"n";
stClient->SetStrategy(new GoWalkStrategy);
stClient->UseStrategy();
}
Результат:
Wake up.
Take shower.
Dress.
Go out.
Go to bus stop.
Wait.
Arrive.
Do work.
Go out.
Go to bus stop.
Arrive.
Do exercises.
Go out.
Go to park.
Walk.
11. http://www.slideshare.net/IgorShkulipa 11
Реализация с функторами. Базовая стратегия
class IStrategy
{
public:
virtual double operator()(double a, double b)=0;
protected:
double Add(double a, double b) {return a+b;}
double Sub(double a, double b) {return a-b;}
double Div(double a, double b) {return a/b;}
double Mul(double a, double b) {return a*b;}
};
12. http://www.slideshare.net/IgorShkulipa 12
Реализация с функторами. Конкретные стратегии
class A2PlusB2Strategy: public IStrategy {
public:
double operator()(double a, double b) {
return Add(Mul(a,a),Mul(b,b));
}};
class A2MinusB2Strategy: public IStrategy {
public:
double operator()(double a, double b) {
return Sub(Mul(a,a),Mul(b,b));
}};
13. http://www.slideshare.net/IgorShkulipa 13
Реализация с функторами. Клиенты стратегий
class IStrategyClient
{
public:
virtual void PrintUsingStrategy
(double a, double b, IStrategy* st)=0;
};
class StrategyClient: public IStrategyClient
{
public:
StrategyClient(){}
void PrintUsingStrategy(double a, double b, IStrategy* st)
{
cout<<a<<" "<<b<<" "<<(*st)(a,b)<<"n";
}
};
14. http://www.slideshare.net/IgorShkulipa 14
Реализация с функторами. Использование
int main(int argc, char* argv[])
{
StrategyClient* stc=new StrategyClient();
stc->PrintUsingStrategy(5,4,new A2MinusB2Strategy());
stc->PrintUsingStrategy(5,4,new A2PlusB2Strategy());
return 0;
}
5 4 9
5 4 41
15. http://www.slideshare.net/IgorShkulipa 15
Адаптер
Паттерн Adapter, представляет собой программную обертку над
существующими классами, преобразуя их интерфейсы к виду,
пригодному для последующего использования.
Пусть класс, интерфейс которого нужно адаптировать к нужному
виду, имеет имя Adaptee. Для решения задачи преобразования
его интерфейса паттерн Adapter вводит следующую иерархию
классов:
◦ Виртуальный базовый класс Target. Здесь объявляется
пользовательский интерфейс подходящего вида. Только этот
интерфейс доступен для пользователя.
◦ Производный класс Adapter, реализующий интерфейс Target.
В этом классе также имеется указатель или ссылка на
экземпляр Adaptee. Паттерн Adapter использует этот
указатель для перенаправления клиентских вызовов в
Adaptee. Так как интерфейсы Adaptee и Target несовместимы
между собой, то эти вызовы обычно требуют
преобразования.
16. http://www.slideshare.net/IgorShkulipa 16
Пример. Преобразование строки в структуру
class InputStringFullName {
protected:
string _strText;
public:
InputStringFullName() {
_strText="";
}
void Input() {
cout<<"Input Full Name (Surname Name MiddleName): ";
char* cstr=new char;
cin.getline(cstr, 9999, 'n');
_strText=string(cstr);
}
string GetText(){return _strText;}
};
19. http://www.slideshare.net/IgorShkulipa 19
Использование адаптера
int main()
{
InputStringFullName* isfn=new InputStringFullName();
isfn->Input();
FullNameAdapter* fna=new FullNameAdapter(isfn);
FullName* fn=fna->GetFullName();
fn->Print();
}
Результат:
Input Full Name (Surname Name MiddleName): Ivanov Petr Sidorovich
Name: Petr
Middle Name: Sidorovich
Surname: Ivanov
20. http://www.slideshare.net/IgorShkulipa 20
Адаптеры STL
Адаптеры - шаблонные классы, которые обеспечивают отображения
интерфейса. Например, insert_iterator обеспечивает адаптер с
интерфейсом итератора вывода.
Адаптеры контейнеров:
• Часто бывает полезно обеспечить ограниченные интерфейсы
контейнеров. Библиотека предоставляет stack, queue и
priority_queue через адаптеры, которые могут работать с
различными типами последовательностей.
Адаптеры функций:
• Функциональные адаптеры работают только с классами
функциональных объектов с определёнными типами
параметров и типом результата.
Адаптеры итераторов:
• Обратные итераторы
• Итераторы вставки
22. http://www.slideshare.net/IgorShkulipa 22
Алгоритмы
В STL существует множество готовых алгоритмов. Они позволяют
сортировать данные в массиве, искать в нем какое-либо значение,
менять элементы местами и т.д. Для их работы необходимо
подключить <algorithm> в начале программы.
Алгоритмы реализованы в виде функций библиотеки.
23. http://www.slideshare.net/IgorShkulipa 23
Микро-алгоритмы
• swap(T &a, T &b) - Меняет местами значения двух элементов.
• iter_swap(It p, It q) - Меняет местами значения элементов, на
которые указывают итераторы.
• max(const T &a, const T &b ) - Возвращает максимальный
элемент.
• min(const T &a, const T &b ) - Возвращает минимальный
элемент.
У этих алгоритмов есть версии с тремя параметрами. Третий параметр
принимает бинарный предикат, задающий упорядоченность
объектов.
24. http://www.slideshare.net/IgorShkulipa 24
Алгоритмы, не модифицирующие последовательности
• size_t count(It p, It q, const T &x) - Возвращает, сколько раз
элемент со значением x входит в последовательность, заданную
итераторами p и q.
• size_t count_if(It p, It q, Pr pred) - Возвращает, сколько раз
предикат pred возвращает значение true.
Например, count_if(p, q, divides_by(8)) вернет, сколько элементов
кратно 8;
25. http://www.slideshare.net/IgorShkulipa 25
Алгоритмы поиска
• find(It p, It q, const T &x) - Возвращает итератор на первое
вхождение элемента x в последовательность, заданную итераторами p
и q.
• find_if(It p, It q, Pr pred) - Возвращает итератор на первый
элемент, для которого предикат pred вернул значение true.
• find_first_of(It p, It q, Itr i, Itr j) - Возвращает итератор на
первое вхождение любого элемента из последовательности, заданной
итераторами i и j, в последовательность, заданную итераторами p и q.
Последовательности могут быть разных типов (например std::vector и
std::list).
• min_element(It p, It q) - Возвращает итератор на минимальный
элемент последовательности.
• max_element(It p, It q) - Возвращает итератор на максимальный
элемент последовательности.
• equal(It p, It q, Itr i) - Сравнивает две последовательности на
эквивалентность. Вторая последовательность задается одним
итератором, так как последовательности должны быть одинаковой
длины. Если вторая короче, то undefined behaviour.
26. http://www.slideshare.net/IgorShkulipa 26
Алгоритмы поиска
• pair <It, Itr> mismach(It p, It q, Itr i) - Возвращает пару
итераторов, указывающую на первое несовпадение
последовательностей.
• for_each(It p, It q, F func) - Для каждого элемента
последовательности применяет функтор func. Возвращаемое значение
функтора после каждого применения игнорируется.
• bool binary_search(It p, It q, const T &x) - Возвращает true,
если в упорядоченной последовательности есть элемент, значение
которого равно x, false в противном случае.
• Если хотим получить итератор на элемент со значением x, то нужно
использовать алгоритмы lower_bound(It p, It q, const T &x),
upper_bound(It p, It q, const T &x), equal_range(It p, It q,
const T &x), которые выполняют то же, что и одноименные методы
для контейнера std::set.
Все эти алгоритмы имеют версии с параметром, принимающим бинарный
предикат, задающий упорядоченность объектов.
27. http://www.slideshare.net/IgorShkulipa 27
Модифицирующие алгоритмы
• fill(It p, It q, const T &x), fill_n(It p, Size n, const T &x) -
Заполняют последовательность значениями, равными значению x.
• generate(It p, It q, F gen), generate_n(It p, Size n, F gen) - Заполняют
последовательность значениями, сгенерированными функтором gen (например,
генератором случайных чисел).
• random_shuffle(It p, It q) - Перемешивает элементы в случайном порядке.
• copy(It p, It q, Itr out) - Копирует значения элементов
последовательности, заданной итераторами p и q, в последовательность,
начинающуюся с итератора out.
• copy_backward(It p, It q, Itr out) - Копирует элементы
последовательности, заданной итераторами p и q, в последовательность,
заканчивающуюся итератором out.
• remove_copy(It p, It q, Itr out, const T &x) - Копирует значения
элементов из последовательности, заданной итераторами p и q, в
последовательность, начинающуюся с итератора out, за исключением элементов,
значения которых равны значению x.
• remove_copy_if(It p, It q, Itr out, Pr pred) - Копирует значения
элементов из последовательности, заданной итераторами p и q, в
последовательность, начинающуюся с итератора out, за исключением элементов,
для которых предикат pred возвращает значение true.
• reverse(It p, It q) - Переставляет элементы в обратном порядке.
• reverse_copy(It p, It q, Itr out) - Копирует значения элементов в обратном
порядке.
• rotate(It p, It middle, It q) - Сдвигает элементы последовательности так,
что элемент, на который указывает итератор middle становится первым.
28. http://www.slideshare.net/IgorShkulipa 28
Модифицирующие алгоритмы
• remove(It p, It q, const T &x) - Удаляет из последовательности элементы,
значения которых совпадают по значению с x. Возвращает итератор на новый
конец последовательности.
• unique(It p, It q), unique(It p, It q, Pr pred) - Удаляет одинаковые
подряд идущие элементы, оставляя только по одному элементу для каждого
значения. Элементы последовательности должны быть отсортированы. Работает
аналогично алгоритмам remove и remove_if, оставляя в начале только
уникальные элементы, а в конце - то, что осталось. В качестве третьего
параметра можно передавать предикат, сравнивающий два элемента и
возвращающий true, если элементы равны, и false в противном случае.
• unique_copy(It p, It q, Itr out), unique_copy(It p, It q, Itr out, Pr
pred) - Копирует уникальные элементы в последовательность, начинающуюся с
итератора out.
• transform(It p, It q, Itr out, F func) - К каждому элементу входящей
последовательности применяет функтор func и записывает результат в
последовательность, начинающуюся с итератора out.
• accimulate(It p, It q, T i, F func) - Последовательно применяет бинарный
функтор func к парам (i, *p++), где i - некоторое начальное значение, которое
затем каждый раз заменяется значением, которое возвращает функтор. Функтор
должен возвращать значение типа T.
29. http://www.slideshare.net/IgorShkulipa 29
Модифицирующие алгоритмы
• sort(It p, It q), sort(It p, It q, Pr pred) - Сортирует элементы
последовательности в порядке возрастания.
• stable_sort(It p, It q), stable_sort(It p, It q, Pr pred) - Сортирует
элементы, сохраняя порядок элементов с одинаковыми значениями относительно
друг друга. Эти алгоритмы требуют итераторов произвольного доступа, поэтому
на списке работать не будут. Но у списка есть собственные функции члены sort,
stable_sort.
• void nth_element(It p, It nth, It q), void nth_element(It p, It q, It
nth, Pr pred) - Позволяет получить n-й по порядку элемент (n-й по счету, как
если бы массив был отсортирован), переставляя элементы таким образом, что
все элементы до него меньше, либо равны ему, а элементы после - больше, либо
равны ему.
• partition(It p, It q, Pr pred) - Переставляет элементы последовательности
таким образом, что все элементы, для которых предикат вернул true,
предшествуют тем, для которых он вернул false. Возвращает итератор на первый
элемент из второй группы.
• void partial_sort(It p, It middle, It q), void partial_sort(It p, It
middle, It q, Pr pred) - Переставляет элементы последовательности так, что
элементы межу итераторами p и q располагаются в том порядке, как если бы
последовательность была отсортирована, а элементы в оставшейся части - в
произвольном порядке. То есть получаем часть отсортированной
последовательности (не то же самое, что отсортированную часть).
• merge(It p, It q, Itr i, Itr j, Iter out), merge(It p, It q, Itr i, Itr
j, Iter out, Pr pred) - Сортирует две последовательности слиянием.
30. http://www.slideshare.net/IgorShkulipa 30
Лабораторная работа №2. Функторы, адаптеры,
алгоритмы
Создать парсер командной строки, реализующий команды для работы со
стандартными алгоритмами.
Например:
➢ sort 2 4 1 8 3 5 – сортирует заданный массив элементов
➢ sum 10 5 8 11 – сумма указанных чисел
➢ average 7 8 3 4 – среднее указанных чисел
➢ find 6 in 5 8 7 3 6 9 2 – находит номер указанного элемента
➢ и т.д. 10+ алгоритмов
Выполнить реализацию с использованием паттернов «Стратегия на
функторах» и «Адаптер».
Реализовать приложение на основе паттерна «Singleton».
Парсер команд удобно реализовывать на основе контейнеров «Очередь»
или «Стек».