390 likes | 563 Views
Про зачет. Про зачет и т.д. - 1. 11 задач На сайте скоро будут возможные темы задач Что-то про наследование Что-то про string ( собственный) или list Про map Про value_type или decltype Про лямбда … и еще …. Баллы … 20 9 задач 30 8 задач 40 7 задач
E N D
Про зачет и т.д. - 1 • 11 задач • На сайте скоро будут возможные темы задач • Что-то про наследование • Что-то про string (собственный) или list • Про map • Про value_type или decltype • Про лямбда • … и еще … • Баллы … • 20 9 задач • 30 8 задач • 40 7 задач • 50 6 задач • 60 5 задач • 2 мая занятий не будет, но, скорее всего, будут доп.слайды и доп.задачи – только для тех, кто хочет набрать много баллов
Про зачет и т.д. - 2 • Можно пользоваться любыми материалами • Нельзя пользоваться чьей то помощью, в любом виде. Это по возможности будет проверятся. И если будет обнаружено, что кто-то пользуется помощью, зачет для этого человека будет окончен и все задачи не засчитаны (( • Сдавать можно как угодно (на листочках, на каком-нибудь носителе) • Можно сдавать по частям, например 4 задачи в один день и 5 – в другой (каждая задача засчитывается отдельно) • Первая попытка (предварительная): • 16 мая на 2 часе • потом 23 мая • (м.б. еще одна до 23? – посмотрим по результатам 16 мая)
Среднее арифметическое положительных чисел – с помощью decltype template <class T> auto averagepos(T b, T e) -> decltype(*b/2.0) { decltype(*b/2.0) sum = 0; int n = 0; for (T p = b; p != e; p++) { if(*p > 0) { sum+=*p; n++; } } return sum / n; } vector<double> v; cout << averagepos (v.begin(), v.end());
month.h const char* monthName(int month); month.cpp #include "month.h" #include <cassert> namespace { const char* monthNames[12] = { "January“, "February", … "December", } }; const char* monthName(int month) { assert(month >= 1 && month <=12); return monthNames[month-1]; } main.cpp #include "month.h" int main() { cout << monthName(4); Типичная ошибка:using namespace std; в h файлеЛучше не писать! Задача 1: Название месяца
Задача 3: В каждом векторе хотя бы одно положительное число vector<vector<int>> vv; … bool b = all_of(vv.begin(), vv.end(), [] (const vector<int>& v) { return any_of(v.begin(), v.end(), [] (int i) { return i > 0; }); } ); • Я бы, наверное, выписывал условия отдельно auto pos = [] (int i){ return i > 0; }; auto anyPos = [&] (const vector<int>& v) { return any_of(v.begin(), v.end(), pos); }; bool b = all_of(vv.begin(), vv.end(), anyPos);
Вариант с union union { double x; int a[2]; }; x = 2.71828; cout << a[0] << " " << a[1]; Вариант с преобразованием указателя x = 2.71828; int* p = (int*)&x; cout << p[0] << " " << p[1]; Или лучше так: int* p = reinterpret_cast<int*>(&x); Задача 4: из каких int состоит double?
Эту задачу вы можете решить до следующего занятия, до 16.5 Пока только пример использования // полиморфный массив vector<person*> v; v.push_back(new student(“Петров“,341)); v.push_back(new professor(“Сидоров“,“алгебра“)); v.push_back(new student(“Чижиков“,342)); … // Печатаем double sum = 0; for (auto p=v.begin(); p!=v.end(); p++) { cout << *p->getName(); } Массив, который может содержать значения разных типов. Благодаря виртуальным функциям мы объекты разных типов можем обрабатывать единообразно Задача 5: Студенты и преподаватели ! ! ! !
// Иерархия классов class person { public: virtual … getName …// Еще методы… }; class student: public person {// Методы… }; class professor : public person { // Методы… }; // Считаем количество студентов int cnt = 0; for (auto p = v.begin(); p != v.end(); p++) { if (typeid(**p) == typeid(student)) cnt++; } Задача 6:Сколько студентов?
Хочется использовать typeid? Скорее всего, надо ввести виртуальную функцию. (smell…) auto id = typeid(*p); if (id == typeid(circle)) cout << "Это круг"; else if (id == typeid(square)) cout << "Это квадрат"; else if (id == typeid(triang)) cout << "Это треугольник"; Придеться менять для каждой новой фигуры… Лучше добавить виртуальную функциюgetName() cout<< "Это" << p->getName(); То есть: Желательно, что бы код, который работает с shape вообще не знал о том, какие есть производные типы, и сколько их Иначе при добавлении нового производного типа все может сломаться Почему плохо использовать RTTI? Это, кстати, называется принцип подстановочности Лисков
Когда все-таки имеет смысл использовать RTTI? Если мы не можем добавить виртуальную функцию в базовый класс.(Это класс из библиотеки, например.)(Например, в MFC классы, производные от CView) Когда это все-таки имеет смысл?
Преобразование из числа в строку (пригодится для тех, кто соберется еще сделать задачу про студентов и преподавателей)
Способ С++ строковые потоки #include <sstream> ostringstreamstr_out; strout << i << " " << x; string s = str_out.str(); И аналогично istringstream.string s; …istringstreamstr_in(s); str_in >> i >> x; C++11 – to_string #include <string> … string s = to_string(i); Более старые способы: atoi, itoa, atof, _fcvt и т.д. Форматный ввод/вывод в т.ч. в строку. char s[20]; sprinf(s, "%10.3lf", x); Преобразования строка число
template <class T> class numeric_limits { public: static T max(); // Достаточно объявления. // Описание можно не задавать, // все равно оно не потребуется }; // Специализации template<> // Для int inline int numeric_limits<int>::max() { return 0x7FFFFFFF; } template<> // Для unsigned inline unsigned numeric_limits<unsigned>::max() { return 0xFFFFFFFF; } template<> // Для short inline short numeric_limits<short>::max() { return 0x7FFF; } // Пример вызова cout << numeric_limits<int>::max(); cout << numeric_limits<unsigned>::max(); cout << numeric_limits<short>::max(); Задача 2: numeric_limits
Trait – класс свойств Фактически задается функция int 0x7FFFFFFF unsigned 0xFFFFFFFF … и т.д. … Умное слово.. • Trait (класс свойств) • Шаблон, который для типа определяет его свойства • Обычно содержит только typedef и статические функции. Основной прием при программировании шаблонов... (Если у вас непонятная проблема с шаблонами – попробуйте traits…) Замечание: Если бы int, unsigned и т.д. были бы типами, и мы могли быопределять для них методы – все было бы проще…
Шаблоны и функции с переменным числом аргументов 19
Шаблоны с переменным количеством параметров (variadic templates) • С++11 (Visual Studio 2013) • Цель print(1, 2, 3); print(1,2,3,4,5,6); print(3.14, 2.7); print(1, "abc", 3.14); • Решение template <class T1, class ... T> void print(const T1& first, const T& ... other) {cout << first << "\n"; print(other...); } void print() { } • class … T – может быть сколько угодно параметров (может быть, 0)(parameter pack) • T … params – то же в заголовке функции • params… - список параметров передаем в какую-то другую функцию Вызываем рекурсивно, но параметров уже меньше А это чтобы рекурсия завершилась
Еще один способ - initializer_list #include <initializer_list> cout << print({1,2,3,4}); template <class T> void print(const T& lst) { for(auto p = lst.begin(); p!= lst.end(); p++)& cout << *p << “\n”; } • А также можно использовать va_arg и т.д., но это морально устревший способ
shared_ptr • Указатели со счетчиком ссылок #include <memory> { shared_ptr<int> p(new int(56)); … { shared_ptr<int> p1 = p; … // Два указателя на 56 }// Теперь только один … } // Теперь нет указателей. Память освобождается • Для управления разделяемыми объектами
Как shared_ptr, но без разделения ссылок) Например: когда лень писать в деструкторе delete class abc { klm* p; public: abc() : p(new klm(56)) {} ~abc() { delete p; } }; А присваивание и к-р копирования? Можно так: class abc { unique_ptr<int> p; public: abc() : p(new int(56)) {} // При уничтожении p //delete автоматически. // Т.е. достаточно деструктора // по умолчанию }; присваивание и копирование автоматически запретятся Еще есть weak_ptr unique_ptr
Препроцессор 25
На самом деле в C++ два языка • До компилятора работает препроцессор • Текст промежуточный файл (тоже текст на C++) • Результат работы препроцессора потом обрабатывается компилятором • Есть опция Generate preprocessed file – можно посмотреть промежутoчный файл. Язык препроцессора: • Очень простой • Почти ничего не знает о синтаксисе C++ • Конечно, совсем ничего не знает о значения переменных и т.д. • Считается, что лучше использовать поменьше (Я в общем, согласен, но иногда удобно…)
Директивы препроцессора • На отдельной строке • Начинаются с # Например: #define ABC 123 #include <map> • Если не помещается на одну строку: символ \ на конце строки. Например: #define ABC \ 123 Замечание: После \ не должно быть даже пробелов!
#define ABC 123 Заменить всюду в тексте ABC на 123 Принято использовать заглавные буквы Может быть с параметрами #define SQR(x) x * x Заменить всюду в тексте SQR(что-то) на что-то * что-то Например:SQR(a[i]) a[i] * a[i] Может быть без правой части #define ABC(x) Всюду заменить ABC(что-то) на пустую строку (т.е. выкинуть). #define
Вместо const #define SIZE 100 const int size = 100; (или enum) Вместо inline или template #define SQR(x) x*x template <class T>inline T sqr(T x) { return x*x; } Где не надо использовать #define
Когда имеет смысл использовать #define? • Если хотим быстро и эффективно добавлять / убирать какой-то код (для отладки) #define TRACE(x) cout << (x) TRACE(a[i]); cout << (a[i]); Когда отладим, просто напишем: #define TRACE(x) и все отладочные печати вообще исчезнут из кода • Если то, что мы хотим записать в define – не функции. (пример на следующем слайде)
template<> inline int numeric_limits<int>::max() { return 0x7FFFFFFF; } template<> inline unsigned numeric_limits <unsigned>::max() { return 0xFFFFFFFF; } … // Вариант с препроцессором #define NUMLIM(type, val) \ … какое-то определение … NUMLIM(int, 0x7FFFFFFF); NUMLIM(unsigned, 0xFFFFFFFF); … Когда имеет смысл использовать #define? Пример
Опасности (почему #define хуже inline функции) #define SQR(x) x*x • Проблема: приоритет операций SQR(x+1) x+1 * x +1 Решение: • Взять все в скобки • И аргументы взять в скобки #define SQR(x) ((x)*(x)) • Еще проблема (неразрешимая..): SQR(i++)
Оператор # • # x - stringize Взять x в кавычки. #define ABC(x) #x ABC(klmn) “klmn" Удобно в отладочных макро: #define TRACE(x) cout << #x " = " << (x) TRACE( 2*a[i+j] ) cout << "2*a[i+j]" " = " << (2*a[i+j])
## - склейкаa ## x – записать a и значение x подряд, без пробела. #define ABC(x) a ## x ## b ABC(klmn) aklmnb Пример (не убедительный…): #define MYDEFS(x) \ int i##x; \ string s##x; \ double dbl##x; MYDEFS(Student) int iStudent; string sStudent; double dblStudent; Оператор ##
Д.з 35
Д.з. 1 • Пусть я хочу для данного типа проверять, это указатель на что-то (что угодно) или не указатель. Т.е. я хочу описать что-то, похожее на логическую функцию, которая, например, для int* возвращала бы true, адля double – false. Попробуйте, пожалуйста, описать что-то такое и приведите примеры использования. • Замечание: Это похоже на функцию, но не может быть просто функцией – ее параметр не значение, а тип. • Подсказка: trait • Замечание: на самом деле, есть такой стандартный шаблон, можно просто его найти в документации и показать, как он используется, это будет засчитано. 1а. Задачу 1 относительно легко решить, если под указателями подразумевается все, что описано, как что-то*. А если подразумевать под указателями все, для чего определена операция *? Например shared_ptr. Я думаю, что это тоже возможно, если использовать довольно хитрый прием, который называется SFINAE. Но, честно скажу, так сразу не могу написать. Если кто-то сделает задачу в таком варианте, получит еще 2 балла. Но в этой задаче нельзя использовать стандартные шаблоны 36
Д.з. 2 • Описать шаблон функции, которая ищет максимум для любого количества параметров и для любых числовых типов. Примеры вызова: cout << maximum(1, 2, 3); cout << maximum(5.6, 7.1, 10.0, 2.2, 123); Если вы напишете вариант с использованием initalizer_list(там интерфейс будет, понятно, немного другой), то за это будет еще 0.5 балла. 37
Д.з. - 3 • Снова задача про RAII. Была примерно такая задача, на 4 занятии void f(stack& s){klm* p = new klm(100); // Динамически отводим какой-то обьект // … тут, допустим, мы что-то делаем с p и scout << s.pop(); // … а тут еще что-то делаем с p и s delete [] p; // Потенциальная утечка памяти: delete может и не вызваться! } Покажите, как справиться с возможной проблемой утечки памяти с помощью unique_ptr. (Это, на самом деле, очень простая задача, буквально одна строчка) 38
Д.з. - 4 • Реализовать NUMLIM (см. пример выше в слайдах) • Пример на использование shared_ptr Я хочу вести адресную книгу. Как выяснилось, у меня много знакомых, которые живут в одном и том же месте. Поэтому я решил, что будет удобно, если при добавлении нового человека, можно будет адресне указывать, а указать, что он живет там же, где другой, адрес которого я уже задал. В результате я хочу сделать вот что: • Описать структуру или класс address c полями "город","улица", "дом" • В классе addressbook я хочу, чтобы были методы - add(имя, город, улица, дом) - addNeighbour(имя нового человека, имя)- remove(имя) – удалить человека из книги - printaddres(имя) – напечатать адрес человка (В последних 2 методах будем считать что 'имя' точно у нас уже записано, это можно не проверять). В качестве представления я бы хотел использовать map, где ключ – это имя, а значение – shared_ptr на адрес (shared_ptr, чтобы не хранить адрес несколько раз). Попробуйте написать такой класс. 39