30
Темы лекции: Распределители, функторы, адаптеры, алгоритмы. Практическое задание: Функторы, адаптеры, алгоритмы. Тренер: Игорь Шкулипа, к.т.н. С++ Библиотеки STL и Qt. Занятие 2

C++ STL & Qt. Занятие 02

Embed Size (px)

Citation preview

Темы лекции: Распределители, функторы, адаптеры, алгоритмы.

Практическое задание: Функторы, адаптеры, алгоритмы.

Тренер: Игорь Шкулипа, к.т.н.

С++ Библиотеки STL и Qt. Занятие 2

http://www.slideshare.net/IgorShkulipa 2

Распределители памяти

Каждый контейнер имеет распределитель памяти (allocator),который используется при выделении памяти под элементы контейнера ипредназначен для того, чтобы освободить пользователей контейнеров, отподробностей физической организации памяти.

Стандартная библиотека обеспечивает стандартный распределительпамяти, заданный стандартным шаблоном allocator из заголовочногофайла <memory>, который выделяет память при помощи операции new( ) и по умолчанию используется всеми стандартными контейнерами.

Класс allocator обеспечивает стандартные способы выделения иперераспределения памяти, а также стандартные имена типов дляуказателей и ссылок.

Пользователь может задать свои распределители памяти,предоставляющие альтернативный доступ к памяти.

Стандартные контейнеры и алгоритмы получают память и обращаются кней через средства, обеспечиваемые распределителем памяти.

http://www.slideshare.net/IgorShkulipa 3

Класс распределителя

template <class T> class allocator {

public:

typedef T* pointer;

typedef const T* const_pointer;

typedef T& reference;

typedef const T& const_reference;

typedef T value_type;

typedef size_t size_type;

typedef ptrdiff_t difference_type;

allocator(); ~allocator();

pointer address(reference x);

const_pointer const_address(const_reference x);

pointer allocate(size_type n);

void deallocate(pointer p);

size_type init_page_size();

size_type max_size();

};

class allocator<void> {

public:

typedef void* pointer;

allocator();

~allocator();

};

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);

}

}

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>());

http://www.slideshare.net/IgorShkulipa 6

Strategy

Паттерн Strategy предназначенный для определения семействаалгоритмов и инкапсуляции каждого из них и обеспечения ихвзаимозаменяемости.

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

http://www.slideshare.net/IgorShkulipa 7

Пример

class IStrategy

{

public:

virtual void Use()=0;

protected:

void WakeUp() {cout<<"Wake up.\n";}

void Shower() {cout<<"Take shower.\n";}

void Dress() {cout<<"Dress.\n";}

void GoToBusStop() {cout<<"Go to bus stop.\n";}

void Wait() {cout<<"Wait.\n";}

void Arrive() {cout <<"Arrive.\n";};

void DoWork() {cout <<"Do work.\n";}

void DoExercises() {cout <<"Do exercises.\n";}

void Walk() {cout<<"Walk.\n";}

void GoOut() {cout<<"Go out.\n";}

void GoToPark() {cout<<"Go to park.\n";};

};

http://www.slideshare.net/IgorShkulipa 8

Классы конкретных стратегий

class GoToWorkStrategy: public IStrategy {

public:

virtual void Use() {

WakeUp(); Shower(); Dress(); GoOut();

GoToBusStop(); Wait(); Arrive(); DoWork();

}};

class GoWalkStrategy: public IStrategy {

public:

virtual void Use() {

GoOut(); GoToPark(); Walk();

}};

class GoToGymStrategy: public IStrategy {

public:

virtual void Use() {

GoOut(); GoToBusStop(); Arrive(); DoExercises();

}};

http://www.slideshare.net/IgorShkulipa 9

Клиент стратегий

class IStrategyClient

{

public:

virtual void UseStrategy()=0;

virtual void SetStrategy(IStrategy* st){_strategy=st;};

protected:

IStrategy* _strategy;

};

class StrategyClient1: public IStrategyClient

{

public:

StrategyClient1(){}

void UseStrategy()

{

_strategy->Use();

}

};

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.

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;}

};

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));

}};

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";

}

};

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

http://www.slideshare.net/IgorShkulipa 15

Адаптер

Паттерн Adapter, представляет собой программную обертку надсуществующими классами, преобразуя их интерфейсы к виду,пригодному для последующего использования.

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

◦ Виртуальный базовый класс Target. Здесь объявляетсяпользовательский интерфейс подходящего вида. Только этотинтерфейс доступен для пользователя.

◦ Производный класс Adapter, реализующий интерфейс Target.В этом классе также имеется указатель или ссылка наэкземпляр Adaptee. Паттерн Adapter использует этотуказатель для перенаправления клиентских вызовов вAdaptee. Так как интерфейсы Adaptee и Target несовместимымежду собой, то эти вызовы обычно требуютпреобразования.

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;}

};

http://www.slideshare.net/IgorShkulipa 17

Класс структуры

class FullName {

private:

string _strName; string _strSurname; string _strMiddle;

public:

FullName(string strSurname, string strName, string strMiddleName) {

_strName=strName; _strSurname=strSurname; _strMiddle=strMiddleName;

}

void Print() {

cout<<"Name: "<<_strName<<"\n";

cout<<"Middle Name: "<<_strMiddle<<"\n";

cout<<"Surname: "<<_strSurname<<"\n";

}

string GetName() {return _strName;}

string GetSurname() {return _strSurname;}

string GetMiddle() {return _strMiddle;}

void SetName(string strText) {_strName=strText;}

void SetSurname(string strText) {_strSurname=strText;}

void SetMiddle(string strText) {_strMiddle=strText;}

};

http://www.slideshare.net/IgorShkulipa 18

Класс-адаптер

class FullNameAdapter: public InputStringFullName

{

public:

FullNameAdapter(InputStringFullName* stringFullName)

{

_strText=stringFullName->GetText();

}

FullName* GetFullName()

{

unsigned iFirstSpace=_strText.find_first_of(" ");

string strSurname=_strText.substr(0,iFirstSpace);

unsigned iSecondSpace=

_strText.substr(iFirstSpace+1).find_first_of(" ")+iFirstSpace+1;

string strName=_strText.substr(iFirstSpace, iSecondSpace-iFirstSpace);

string strMiddle=_strText.substr(iSecondSpace);

return new FullName(strSurname, strName, strMiddle);

}

};

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

http://www.slideshare.net/IgorShkulipa 20

Адаптеры STL

Адаптеры - шаблонные классы, которые обеспечивают отображенияинтерфейса. Например, insert_iterator обеспечивает адаптер синтерфейсом итератора вывода.

Адаптеры контейнеров:

• Часто бывает полезно обеспечить ограниченные интерфейсыконтейнеров. Библиотека предоставляет stack, queue иpriority_queue через адаптеры, которые могут работать сразличными типами последовательностей.

Адаптеры функций:

• Функциональные адаптеры работают только с классамифункциональных объектов с определёнными типамипараметров и типом результата.

Адаптеры итераторов:

• Обратные итераторы• Итераторы вставки

http://www.slideshare.net/IgorShkulipa 21

Пример. Адаптер «Стек»template <class Container>

class stack {

friend bool operator==(const stack<Container>& х, const stack<Container>& y);

friend bool operator<(const stack<Container>& х, const stack<Container>& y);

public:

typedef Container::value_type value_type;

typedef Container::size_type size_type;

protected:

Container c;

public:

bool empty() const { return c.empty(); }

size_type size() const { return c.size(); }

value_type& top() { return c.back(); }

const value_type& top() const { return c.back(); }

void push(const value_type& х) { с.push_back(х); }

void pop() { c.pop_back(); }

};

template <class Container>

bool operator==(const stack <Container>& х, const stack<Container>& y)

{ return х.с == у.с;}

template <class Container>

bool operator<(const stack<Container>& х, const stack<Container>& y)

{ return х.с < у.с; }

http://www.slideshare.net/IgorShkulipa 22

Алгоритмы

В STL существует множество готовых алгоритмов. Они позволяютсортировать данные в массиве, искать в нем какое-либо значение,менять элементы местами и т.д. Для их работы необходимоподключить <algorithm> в начале программы.

Алгоритмы реализованы в виде функций библиотеки.

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 ) - Возвращает минимальный

элемент.

У этих алгоритмов есть версии с тремя параметрами. Третий параметрпринимает бинарный предикат, задающий упорядоченностьобъектов.

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;

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.

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.

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

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 становится первым.

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.

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) - Сортирует две последовательности слиянием.

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».

Парсер команд удобно реализовывать на основе контейнеров «Очередь»или «Стек».