560 likes | 755 Views
Еще раз про экзамен. 22 мая – 10 :00 Сбор у баобаба. Для тех, кто придет позже – будет объявление на расписании 3 курса Вопрос 33 ( RTTI и typeid) вычеркивается Еще одна возможность сдать спец.курс будет в официальный день сдачи, 6 июня, в 10:00, сбор у баобаба. Задачи на 12 мая.
E N D
Еще раз про экзамен • 22 мая – 10:00Сбор у баобаба. Для тех, кто придет позже – будет объявление на расписании 3 курса • Вопрос 33 (RTTI и typeid) вычеркивается • Еще одна возможность сдать спец.курс будет в официальный день сдачи, 6 июня, в 10:00, сбор у баобаба
Задачи на 12 мая Язык С++ - 2
Вариант с union union { double x; int a[2]; }; x = 2.71828; cout << a[0] << " " << a[1]; Вариант с преобразованием указателя x = 2.71828; int* p = (int*)&x; cout << a[0] << " " << a[1]; Или лучше так: int* p = reinterpret_cast<int*>(&x); (см. ниже) Задача: из каких int состоит double?
Задачи на 19мая Язык С++ - 4
ASSERT #ifdef DEBUG #define ASSERT(cond) \ if (!(cond)) \ cout << "Условие " #cond "не выполняется"; #else #define ASSERT(cond) #endif
Cписок ошибок #define ERROR_TABLE map<int, string> errors; #define ERROR(num, message) errors[num] = message; … ERROR_TABLE ERROR(1, “Какая-то проблема“) ERROR(2, “Все плохо..“) ERROR(3, “У вас все неправильно“)
Усложним немного задачу: Пусть я составил список ошибок, которые у меня будут выдаваться – с номерами и текстом ошибок. Но номера я записываю не как числа, а как символьные имена. И я бы хотел, чтобы с этими именами автоматически связывались числа 1, 2… по порядку. Т.е. я хочу, чтобы я записал список ошибок в каком-то таком виде: ERROR(someError, “Какая-то проблема“) ERROR(allBad, “Все плохо..“) ERROR(allWrong, “Все неправильно“) И автоматически генерировалось что-то такое: еnum{ someError, allBad, allWrong, }; map<int, string> errors; errors[someError] = “Какая-то проблема“; errors[allBad] = “Все плохо..“) errors[allWrong] = “Все неправильно“; Как это сделать? Это будет еще одно домашнее задание, самое последнее, просто для тех, кому это будет интересно.. Еще про задачу о списке ошибок
Прием: разные определения для одного и того же макроса • Подсказка 1 – возможная схема --- errlist.h -------------------------------- ERROR(someError, “Какая-то проблема“) ERROR(allBad, “Все плохо..“) ERROR(allWrong, “Все неправильно“) -- основная программа ----------------- … … определения, чтобы сгенерировать enum #include "errlist.h" … определения, чтобы сгенерировать map #include "errlist.h" • Подсказка 2 – вам может потребоваться оператор препроцессора #undef#undef ABCСмысл: отменить определение ABC – нужно, если мы хотим его переопределить
// Иерархия классов class CShape { public: virtual ~CShape() {} // Еще методы… }; class CSquare : public CShape {// Методы… }; class CCircle : public CShape {// Методы… }; class CRhomb : public CShape {// Методы… }; // Пример создания масива vector<CShape*> v; v.push_back(new CCircle()); v.push_back(new CSquare()); v.push_back(new CRhomb()); … // Считаем количество квадратов int cnt = 0; for (vector<CShape*>::iterator p = v.begin(); p != v.end(); p++) { if (typeid(**p) == typeid(CSquare)) cnt++; } Задача:Сколько квадратов?
Хочется использовать typeid? Скорее всего, надо ввести виртуальную функцию. (smell…) if (typeid(*p) == typeid(CCircle) cout << "Это круг"; else if (typeid(*p) == typeid(CSquare) cout << "Это квадрат"; else if (typeid(*p) == typeid(CTriang) cout << "Это треугольник"; cout << "Это" << p->name(); Когда все-таки имеет смысл использовать? Если мы не можем добавить виртуальную функцию в базовый класс.(Например, классы, производные от CView) Почему плохо использовать RTTI? Когда это все-таки имеет смысл?
class my_comparator { int m; // 10^n public: my_comparator(int n) { // Calculate 10^n m = 1; for (int i = 0; i < n; i++) m *= 10; } bool operator() (int i, int j) { return i % m < j % m; } }; my_comparator cmp(n); sort(v.begin(), v.end(),cmp); или sort(v.begin(), v.end(),my_comparator(n)); Сортировка по последним n цифрам (с помощью функтора)
Снова сумма положительных элементов Так мы написали.. template <class T> typename T::value_type sumpos(T b, T e) { typename T::value_type sum; for (T p = b; p!=e; p++) { if (*p > 0) { sum += *p; } } return sum; } • Просьба заказчика – пусть тип результата всегда int или double • T.e. требуется по одному типу найти другой по правилу: • float, double double • Все остальное int • Легко сказать – как это реализовать? • trait ! Язык С++ 13
Cумма положительных элементов – новое решение template <class T> struct sum_trait { typedef int sum_type; }; template <> struct sum_trait<float> { typedef double sum_type; }; template <> struct sum_trait<double> { typedef double sum_type; }; #define SUM_TYPE \ typename sum_trait \ <typename T::value_type>:: \ sum_type template <class T> SUM_TYPE sumpos(T b, T e) { SUM_TYPE sum = 0; … дальше без изменений … } Язык С++ 14
Вместо существующего оператора преобразования рекомендуется выбрать один из 3 способов. static_cast Мы создаем объект, устроенный внутри по другому, но с тем же смыслом (примерно…) Т.е. “другие биты, тот же смысл…” T.e. раньше писали: int i = (int)x; complex x = (complex)x; Теперь рекомендуется писать: int i = static_cast<int>(x); reinterpret_cast double* px = &x;char* p = (char*) px; Те же биты, другой смысл… Объект тот же, но мы его интерпретируем по другому. Только в 3 случаях: указатель указатель указатель целое целое указатель Т.е. рекомендуется писать: char* p = reinterpret_cast<char*>(px); Оказывается, преобразования бывают 4 видов
const_cast Пусть у нас есть const int* p;Но мы точно знаем, что это не константа (просто неправильно описан параметр и т.д.). И очень надо ее поменять. int* p1 = (int*)p; *p = 55; Т.е. смысл тот же, биты те же. Только задаем или убираем спецификации const Т.е. рекомендуется писать: int* p1 = const_cast<int*>(p); const_cast
shape* p; Пусть мы точно знаем, что p указывает на square square* p1 = (square*)p; p1->… делаем что-то специальное для квадрата (Это называется downcasting) Вообще-то можно проверить, действительно ли это квадрат.. По таблице вирт.функций Только если есть хотя бы одна вирт. функция Должна быть включена опция “Enable Runtime Type Info” Можно написать так: square* p1 = dynamic_cast<square*>(p); if (p1 == 0) cout << "Ошибка!"; dynamic_cast
Тоже для ссылок: shape& sh; square& p1 = dynamic_cast<square&>(sh); Если не то – исключение (bad_cast) Заключение: Все виды cast лучше применять пореже Но, если вы действительно понимаете, зачем это вам надо – ОК, ничего страшного. dynamic_cast - продолжение
Namespace Язык С++ 20
Зачем нужны namespace? Проблема: Пусть есть две библиотеки классов: Библиотека ABCLIB. класс stack … Библиотека KLMLIB. класс stack #include <abclib.h> #include <klmlib.h> stack st; // Ошибка! Решение: В библиотеке ABCLIB: namespace abc {…class stack { … … } То же в KLMLIB: namespace klm {… class stack … } Теперь abc::stack и klm::stack Язык С++ 21
Как обращаться к именам из namespace? Полное имя: abc::stack klm::stack using объявление using abc::stack; stack abc::stack using директива using namespace abc; stack пытается найти в abc Замечания: и using объявление и using директива действуют до конца блока. Совет: в h файлах – только полное имя Язык С++ 22
Еще возможности Инкрементальное определение namespace abc { … определения …}… что-то вне namespace … namespace abc { … еще определения …} Глобальное пространство имен::f() – глобальная функция using ::f; M.б. вложенные abc::klm Язык С++ 23
Еще возможности - 2 Безымянные (анонимные) namespace namespace { int v; …} Генерируется уникальное имя + using директива namespace <уник.имя> { int v; …} using namespace <уник.имя>; Означает: Переменную v можно использовать только от того места, где она определена, и до конца файла - примерно то же, что static Язык С++ 24
Что мы не прошли… Язык С++ - 25
Множественное наследование Язык С++ - 26
class Car {// Автомобиль … }; class Ship {// Корабль … }; class Amphibia : public Car, public Ship { … }; В реальности с множественным наследованием обычно получается довольно сложно… Множественное наследование - 1
Множественное наследование - 2 Надо ли использовать? Pазные точки зрения… • Да Например, ATL • Нет Например, MFC • Да, но только для интерфейсов (абстрактныe классов, вообще без кода, только с чисто виртуальными функциями) • (Так в Java, например)
А / \ B1 B2 – наследники A \ / C – наследник B1, B2 diamond problem ромбовидная диаграмма Проблема: class A { protected: int x; void f(); }; Сколько в классе C экземпляров поля x? Хорошо бы один… (например, поле "руль" или "мотор" для амфибии) Vehicle (средство передвижения) поля "руль", "мотор" / \ Car Ship \ / Amphibia Diamond problem (ромбовидная диаграмма)
Виртуальные базовые классы class A { … } class B1 : public virtual A { … }; class B2 : public virtual A { … }; class C : public B1, public B2 { … }; • Теперь x будет в C один раз • Класс А называется "виртуальный базовый класс"
private и protected наследование Язык С++ - 31
Класс group - студенческая группа. Представляется, как вектор из объектов класса student class group { vector<student> vect; public: void add(const student& s) void print() const; … }; void group::add(const student& s) { vect.push_back(st); } void group:: print() const { … for (p = vect.begin(); p != vecn.end(); p++) … } Пример
Можно написать короче class group : vector<student> { public: void add(const student& s) void print() const; … }; void group::add(const student& s) { push_back(st); } void group:: print() const { … for (p = begin(); p != end(); … } Public наследование было бы неправильно! private неследование – унаследованные меиоды становятся private Т.е. снаружи наследование не видно… То же с private наследованием
protected наследование • Примерно аналогично • Встречается еще реже
Разные полезные вещи Язык С++ - 35
atoi, itoa, atof, _fcvt и т.д. строковые потоки ostringstream sout; sout << i << " " << x; string s = sout.str(); И аналогично istringstream. Форматный ввод/вывод в т.ч. в строку. char s[20]; sprinf(s, "%10.3lf", x); Форматный вывод в MFC CString s; s.Format("%10.3lf", x); Преобразования строка число
Функции с переменным числом аргументов • #include <stdarg.h> va_start, va_arg, va_end int mysum(int n, …) Вызов: cout << mysum(2, n, k); cout << mysum(4, i, j, k, l); • Не рекомендуется использовать: Нет проверки параметров компилятором: mysum(2, i, j, k) mysum(2, 3.14, "abc") • Но иногда удобно
& - побитовое "И" | - побитовое "ИЛИ" ^ - побитовое «исключающее ИЛИ» ~ - инвертировать виты << - сдвиг влево << - сдвиг влево Побитовые операциидля целых чисел
Еще возможности для работы с битами • Битовые поля class { int a:3; int b:4; int c:1; }; • vector<bool> • bitset<n> bitset<100> b; b[i] = true; if ( b[i] ) …
Полезные вещи в стандартной библиотеке и в boost Язык С++ - 40
Указатели со счетчиком ссылок #include <memory> using namespace std::tr1; { shared_ptr<int> p(new int(56)); … { shared_ptr<int> p1 = p; … // Два указателя на 56 }// Теперь только один … } // Теперь нет указателей // Память освобождается shared_ptr
Если не хотите думать о том, когда освобождать память class abc { shared_ptr<int> p; public: abc() : p(new int(56)) { } // В деструкторе писать // ничего не надо }; Для управления разделяемыми объектами shared_ptr
Алгоритмы, параметры у которых - функции (или функторы) • find_if bool isPos(int i){ return i > 0;} find_if(v.begin(), v.end(), isPos); • Много такого типа • remove_if • transform • for_each • … и т.д. • Лично я почти никогда не использую…
Итераторы вставки, итераторы потоков copy(v.begin(), v.end(), v1.begin()); • С помощью copy можно дописать в конец copy(v.begin(), v.end(), back_inserter(v1)); • С помощью copy можно записать в файл #include <iterator>… ostream_iterator<int> out_it(cout, “ “); copy(v.begin(), v.end(), out_it);
Еще в boost • boost:: hash_map, hash_set • В C++ 0x – unordered_map, unordered_set • Вообще очень много чего (regex, graphs)
C++0x Язык С++ - 46
Только некоторые из новых возможностей: Инициализация контейнеров списком инициализации: vector<int> v = {3, 7, 11 }; Цикл по контейнеру:vector<int> v;for (x: v) { … Вывод типа (type inference) auto i = 3; auto x = 3.14; auto n = abc(x, y); for (auto p = v.begin(); p != v.end(); p++) C++ 0x – новый стандарт языка
Функция, параметр которой – функция Пишем в параметре прямо код функции find_if(v.begin(), v.end(), [] (int x) { return x > 0; } ); sort(v.begin(), b.end(), [] (int x, int y) { return x % 10 < y % 10; } ); void f(vector<int> v, int n) { int m = 1; for (int i = 0; i < n; i++) m *= 10; sort(v.begin(), b.end(), [] (int x, int y) { return x % m < y % m; } ); } lambdas
template <class T> T max(T x, T y) { return y < x ? x : y; } template <Comparable T> T max(T x, T y) { return y < x ? x : y; } concept Comparable<typename T> { bool operator<(T x, T y); }; Более понятные сообщения об ошибках: complex c1, c2; max(c1, c2); "Error: complex is not Comparable" Обнаружение ошибок еще в шаблоне: template <Comparable T> T max(T x, T y) { … max = x; … "operator = not in Comparable" Concepts
А вот что я сказал бы, если бы еще было время… Язык С++ - 50