440 likes | 525 Views
Предпоследнее д.з. 1. Кто больше всех зарабатывает?. struct person { string name; int salalry; }; vector<person> persons; int max = 0; // Вводим и запоминаем все for (;;) { person p; cin >> p.name; if (p.name == "*") break; cin >> p.salalry; persons.push_back(p);.
E N D
Ктобольше всех зарабатывает? struct person { string name; int salalry; }; vector<person> persons; int max = 0; // Вводим и запоминаем все for (;;) { person p; cin >> p.name; if (p.name == "*") break; cin >> p.salalry; persons.push_back(p); // Ищем максимумif (p.salary> max) max = p.salalry; } // Печатаем for(auto it = persons.begin(); it != persons.end(); it++) { if (it->salalry == max) cout<< it->name; } Варианты: • Один проход • хранить список самых богатых • Стандартные функции • sort • еще?
shared_string class shared_string { char* p; // lenдля краткости не используем // Воспомогaтельные методы // Поставить указатель p на // данную строку и увеличить // в ней счетчик. void set_ptr(char * newp) { p = newp; (*newp)++; } // Уменьшить счетчик в строке, // на которую указывает p. // Если надо, удалить строку. void drop_ptr() { (*p)--; if (*p == 0) { cout << "Deleting " << p + 1; delete [] p; } } public: 3
shared_string - продолжение shared_string(const char* s = "") { p = new char[strlen(s)+2]; *p = 1; strcpy(p + 1, s); } ~shared_string() { drop_ptr(); } shared_string(const shared_string& from) { set_ptr(from.p); } shared_string& operator= (const shared_string& from) { if (p != from.p) { drop_ptr(); set_ptr(from.p); } return *this; } void print() const { cout << p+1; } }; // Конец определения класса 4
shared_string - замечания 1 2 "abc" Называется: reference counting (счетчик ссылок) Похожая вещь: smart pointers (умные указатели). shared_ptrв новой стандартной библиотеке(позже пройдем) Еще вариант – non-intrusive (храним счетчики отдельно от самих объектов) 5
foreach для дерева void foreach(tree* p, function<void (int&)> f) { if (p == nullptr) return; f(p->val); foreach(p->left, f); foreach(p->right, f); } // Пример вызова foreach(t, [] (int& i) { i *= 2; }); foreach(t, [] (int& i) { cout << i; }); // Пример со списком захвата int k; cin >> k; foreach(t, [k] (int& i) { i *= k; });
Пятерки – вариант 1, не совсем правильный map<string, int> fives; string name; int n, grade; cin >> n; for (int i = 0; i<n; i++) { cin >> name >> grade;// Читаем имяи оценкуif (grade == 5)fives[name]++;// Увеличиваем счетчик } // (или можно find и insert) // Печатаем ответ for (auto p = fives.begin(); p != fives.end(); p++) { cout << p->first << " - " << p->second << "\n"; } • Не печатает людей без пятерок..
Пятерки – правильные варианты • Более правильно, например, так: cin >> name >> grade; int& num = fives[name]; // Если человека не было, добавит if (grade == 5) // Если пятерка num++; • Еще вариант: cin >> name >> grade; fives[name] += (grade == 5);
Еще про ассоциативные контейнеры 10
multimap #include <map> … multimap<double, int> m; Может быть несколько значений для одного ключа m.insert(make_pair(4.5, 2)); m.insert(make_pair(4.5, 8)); [] не работает find возвращает первую пару См. также: set multiset unordered_map unordered_set(используют хэш таблицы) boost::multi_index(несколько индексов) 11
Шаблон swp template <class T> void swp(T& x, T& y) { T tmp = x; x = y; y = tmp; } Замечание: T tmp; // Так хуже, чем T tmp = x; tmp = x; - Немного медленнее - Работает только если в классе есть конструктор по умолчанию (более существенно!) Новый критерий качества: про шаблоны мы хотим, чтобы они были, по возможности, более общими. 13
Реализация swp для string class string { char* p; int len; public: friend void swp(string& x, string& y); … }; void swp(string& x, string& y) { swp(x.p, y.p); swp(x.len, y.len); } Используем общий шаблон swp 14
Замечание Когда мы пишем шаблоны – это мы пишем код, который работает для разных типов. Называется: полиморфизм • Статический полиморфизм • (А динамический полиморфизм – это наследование и виртуальные функции) 15
Как лучше писать шаблоны алгоритмов для работы с контейнерами? 16
// Вариант 0 template <class T> void print(T c) { … обязательно надо передавать по ссылке! // Вариант 1a template <class T> void print(T& c) { T::iterator p; for (p = c.begin(); p != c.end(); p++) cout << *p << " "; } // Вариант 1б - исправленный template <class T> void print(T& c) { typename T::iterator p; for (p = c.begin(); p != c.end(); p++) cout << *p << " "; } // Пример вызова list<int> l; vector<string> v; … print(l); print(v); Д.з. про шаблон для печати - 1 17
// Вариант 1в – const correctness template <class T> void print(const T& c) { typename T::const_iterator p; for (p = c.begin(); p != c.end(); p++) cout << *p << " "; } // Вариант 1г – C++11 template <class T> void print(const T& c) { for (auto p = c.begin(); p != c.end(); p++) cout << *p << " "; } // Вариант 1д – C++11 template <class T> void print(const T& c) { for (auto& x: c) cout << x << " "; } Д.з. про шаблон для печати - 2 18
Д.з. про шаблон для печати - 3 // Вариант 2 template <class T> void print(T from, T to) { for (T p = from; p != to; p++) cout << *p << " "; } // Пример вызова vector<int> v; list<string> l; … print(v.begin(), v.end()); print(l.begin(), l.end()); Преимущества: • Можно вызывать не только для всего контейнера print(v.begin() +2, v.end()-3); • Очень мало требует от T Только *, ++, !=. int a[10]; … print(a, a+10); Недостатки: • Дольше писать 19
Пример • max_element(итератор начала, итератор конца); #include <algorithm> … vector<int> v; … Заполняем v … int m = max_element(v.begin(), v.end()); 21
Итераторы и алгоритмы STL • Весь интерфейс алгоритмов - через итераторы Схема: Контейнеры Итераторы Алгоритмы "Алгоритмы и контейнеры хорошо работают вместе, потому что ничего не знают друг о друге“ • Вопрос: а как жеremove(v.begin(), v.end(), k);- на самом деле ничего не удаляет, только перемещает элементы, содержащие k в конец Подробнее про алгоритмы – в следующий раз 22
Стандартный алгоритм sort #include <algorithm> … vector<int> v; … // Заполняем v sort(v.begin(), v.end()); • Замечание: sort работает только для vector и deque 23
Как задать свой порядок sort Третий параметр – функция сортировки. Пример: хотим отсортировать по двум последним цифрам. 123 89 903 45964 903 123 4596489 bool mycmp(int i, int j) { return i % 100 < j % 100; } // Пример вызова vector<int> v; … sort(v.begin(), v.end(), mycmp); Может быть не только функция, но и лямбда выражение 24
Как написать шаблон, который что-то делает с контейнером (вектором, списком и т.д.)
value_type Пусть мыхотим написать шаблон, который ищет сумму элементов контейнера.Есть проблема.. Какой тип переменной для суммы? • T::value_type – тип того, что содержится в контейнере Например, если T – это list<int>, то T::value_type – это int • И тоже для итераторов Если T – это list<int>::iterator, то T::value_type – это int • Используется, в основном, в шаблонах template <class T> typename T::value_type sum(T b, T e) { typename T::value_type s = 0; for (T p = b; p < e;p++) s += * p; …
decltype и альтернативный синтаксис функций тут мы еще не знаем, что такое b decltype(выражение) – тип выражения Например,int x; double y; decltype(x+y) sum = 0; Вместе с decltypeчасто полезен альтернативный синтаксис функций. Вместо тип результата имя(список параметров) теперь можно писать auto имя(список параметров)->тип результата То есть: template <class T> sum(T b, T e) ->decltype(*b) { decltype(*b) s = 0; for (T p = b; p < e;p++) s += * p; Со старым синтаксисом была бы ошибка: template <class T> decltype(*b) sum(T b, T e) 27
Пример: шаблон класса "стек" template <class T> class stack { T stk[100]; int size; public: stack() : size(0) {} void push(const T& x) { size[size++] = x; } T pop { return stk[--size]; } }; Использование stack<int> s1; stack<double> s2; stack<complex> s3; stack<stack<int>> s4; При использовании параметры надо указывать явно 29
Замечания Методы можно описывать вне класса template <class T> void stack<T>::push(const T& x) { ...} В частности, конструктор: template <class T> stack<T>::stack(): size(0) {} 30
Не типовые параметры: template <class T,int maxsize> class stack { T stk[maxsize]; … }; stack<int, 100> s; Такие параметры м.б. только целые (и в некоторых случаях указатели) При вызове д.б. константы Внутри - как константы Параметры по умолчаниюtemplate <class T, int maxsize= 100>class stack { … stack<int> s;// To же, что stack<int, 100> Еще возможности 31
Программы из нескольких исходных файлов 32
Что происходит, когда у нас в проекте несколько файлов? a.cpp b.cpp компилируем... а.obj b.obj linker … a.exe (или a.dll) Как определить что-то в одном файле, а использовать в другом? a.cpp void f(int i) { … } b.cpp … f(56); … // f надо объявить Программа из нескольких файлов -1
a.cpp void f(int i) { … } b.cpp void f(int i);// Сообщаем, что где-то// определена f... … f(56); … // ОК, так все работает, но: // обычно так не делают! Почему? Неудобно, особенно если файлов много При изменениях надо много где исправлять Легко забыть исправить. Программа из нескольких файлов -2
Директива include:#include <имя_файла> смысл: включить в это место текст из файла a.cpp #include “a.h” void f(int i) { … } a.h void f(int i); b.cpp #include “a.h” ... … f(56); … с.cpp #include “a.h” ... … f(42); … Программа из нескольких файлов – обычно пишут так:
a.cpp void ff(int i) { … } b.cpp void ff(int i) { … } // Ошибка линкера // функция определена два // раза a.cpp static void ff(int i) { … } Еще один смысл static - для функций и глобальных переменных: static функциявидна только в своем cpp файле, и не видна снаружи. Если мы хотим использовать функцию только в одном cpp файле.. (static)
Сейчас вместо static рекомендуется писать так: a.cpp namespace { void ff(int i) { … } } b.cpp namespace { void ff(int i) { … } } Смысл примерно как у static –имена не видны из других файлов Безымянный namespace
Что происходит, когда мы пишем безымянный namespace? Безымянные (анонимные) namespace namespace { int v; …} Генерируется уникальное имя + using директива namespace <уник.имя> { int v; …} using namespace <уник.имя>; Означает: Переменную v можно использовать только от того места, где она определена, и до конца файла То есть: Примерно то же, что static И лучше использовать безымянный namespace, a не static 38
Где писать шаблоны? • Хотя шаблоны и очень похожи на функции, но их нельзя один раз скомпилировать, и потом использовать скомпилированными. • Потому что шаблон – это не функция, а "фабрика по производству функций" • Как следствие:Если мы хотим использовать шаблон в нескольких С++ файлах то надо: • Написать его в .h файле (полностью) • В .cpp файлах включать этот .h файл
Замечание: Что будет, если в .h файл включить функцию полностью? • Что будет, если в h файл включить целиком функцию (а не только ее заголовок)? Abc.h void f(inti) { …какой-то код … } a.cpp … #include "abc.cpp" a.cpp … #include "abc.cpp" • Получиться, что у нас функция описана два раза в a.cpp и b.cpp.То-есть это ошибка линкера.
Как же тогда писать inline функции? • Проблема:Но inline функции мы должны писать в h файле. (Иначе, как компилятор сможет выполнить inline подстановку, если он не видит тела функции). • Поэтому ввели дополнительное правило:inline функции по умолчанию имеют атрибут staticЭто сделано специально чтобы их можно было писать в h файлах Abc.h inline intsqr(inti) { return i*i; }
Д.з 42
Д.з. - 1 • Ввести несколько целых чисел пока не будет введен 0 и записать их в вектор.Отсортировать вектор в порядке возрастания сумм цифр с помощью sort и своей функции сравнения. • Задача 1 без sort, с помощью multimap. (Как это сделать мы обсуждали на занятии. Это не сложно, но если идей не будет – пишите, я подскажу). • Описать функцию с параметрами v и n, которая сортирует данныйвектор целых чисел v по последним n цифрам. При этом желательно использовать стандартную функцию sort (и, видимо, лямбда выражения со списком захвата) • Опишите шаблон функции, которая для любого последовательного контейнера (списка, вектора) ищет среднее арифметическое входящих в него положительных элементов.(1.5 балла за 2 варианта – с value_type и decltype) 43
Д.з. – 2 • Шаблон queue - очередь (аналог stack, но first in – first out). // Пример использования queue<int> q; q.push(3); q.push(7);q.push(11); cout << q.pop(); // 3 cout << q.pop(); // 7 • Можно реализовать с использованием vector или list (или deque)(И тогда вы можете сказать, что применили шаблон «адаптер»…) • Или можете считать, что очередь фиксированной длины и реализовать, как массив (примерно как стек в слайдах)