Д.з. на 17 марта Язык С++ - занятие 61
Задача 2: фигуры // shape - общий интерфейс для // фигур class shape { protected: int x, y; public: shape(int x_, int y_) : x(x_), y(y_) {} virtual double area()=0; virtual double perimeter()=0; virtual ~shape() {} // … и еще м.б. draw() и т.д. … }; // Квадрат class square: public shape { int size; public: square(int x_, int y_, int size_) : shape(x_, y_), size(size_) {} double area() { return size*size; } double perimeter() { return 4*size; } };
Задача 2: фигуры - продолжение // Ромб class rhomb: public shape { int w, h; public: rhomb(int x_, int y_, int w_, int h_) : shape(x_, y_), w(w_), h(h_) {} double area() { return 2*w*h; } double perimeter() { return 4*sqrt(w*w+h*h); } }; Oшибка: virtual int area()=0; Не учитываются будущие классы… (extensibility – расширяемость)
Пример использования наследования – полиморфный массив Пример использования // полиморфный массив shape* a[10]; a[0] = new square(10, 10, 5); a[1] = new rhomb(70, 80, 10, 20); a[2] = new square(50, 50, 30); // ищем общую площадь double sum = 0; for (int i=0; iarea(); } Массив, который может содержать значения разных типов. Благодаря виртуальным функциям мы объекты разных типов можем обрабатывать единообразно
Д.з. на 24 марта Язык С++ - занятие 65
Задача 1: ошибка в конструкторе time time::time(int h, int m): hour(h), min(m) { if (h =24 || m =60) throw "Ошибка в конструкторе time"; } // Пример вызова конструктора int i, j; cin >> i >> j; try { time t(i, j); t.print(); } catch (const char* s) { cout
Задача 1: еще вариант #include // Стандартные исключения time::time(int h, int m): hour(h), min(m) { if (h =24 || m =60) throw out_of_range("Oшибка в конструкторе time"); } // Пример вызова конструктора try { time t(i, j); t.print(); } catch (const out_of_range& ex) { cout
Задача 2: дерево struct tree { int val; tree* left; tree* right; tree(int v) : val(v), left(0), right(0) {} }; void print(tree* p) { if (p != 0) { print(p->left); cout val; print(p->right); } } void add(tree* & p, int v) { if (p == 0) { p = new tree(v); } else if (v val) { add(p->left, v); } else { add(p->right, v); } } // Пример вызова tree* t = 0; add(t, 7); add(t, 1); add(t, 9); print(t);
Доп. задачи, первый набор Язык С++ - занятие 69
Задача 4: нули в конец void moveZeroes(int* a, int size) { int* to = a; for (int* from=a; from < a+size; from++) { // Не 0? Передвигаем if (*from != 0) { // Тут можно написать if (to != from) *to = *from; to++; } } // Заполняем нулями до конца for (; to < a+size; to++) *to = 0; }
Еще про исключения Язык С++ - занятие 611
Более сложные возможности throw; void f() { try { … } catch (…) { … какая-то обработка … // Хотим снова бросить // исключение для // дальнейшей обработки throw; } } спецификация throw class stack { void push(int i) throw(char*); // Может бросать только char* Не очень ясно, как эффективно реализовать Может быть несколько через запятую throw() – обещаем вообще не бросать исключения В Visual Studio реализовано только throw()
Какие вообще бывают ошибки и как их обрабатывать? Ошибка, которой в отлаженной программе быть не должно i = *p; // А если p == 0? assert(условие); #include … assert(p != 0); Если условие не выполнится –> сообщение Только в Debug версии Ошибка, которую можно разумно обработать / исправить: Код завершения bool ok = f(); if (ok) Ошибка, для которой разумная обработка – это завершение работы (в каком-то смысле): throw
Константы (продолжение) Язык С++ - занятие 614
Константы и указатели в параметрах функций void myfunc(const int* p) { *p = 5; // Ошибка Обещаем не менять *p внутри функции. Зачем так писать? Ловим ошибки в программе Интерфейс функции становится понятнее (легче тем, кто будет использовать) int a[100]; myfunc(a); // точно a не изменилось const int a[] = {… }; void myfunc1(int* p) {…} myfunc1(a); // Ошибка Если константные объекты передаются по указателю или по ссылке в параметрах д.б. const Язык С++ - занятие 615
Константы и ссылки в параметрах функций Все то же относится и к ссылкам в параметрах. void f(const abc& x) { … } Обещаем не менять x внутри функции Зачем вообще const abc& ? Более эффективно для больших abc Язык С++ - занятие 616
Замечания Иногда пишут: void f(const int i) {…} Нет особого смысла.. const трудно добавлять понемногу / по частям.. const цепляются друг за друга.
Константы в классах Язык С++ - занятие 618
Константные поля class abc { const int n; … abc::abc() : n(100)// Задавать можно только здесь { … abc::abc() { n = 100; // Тут уже задавать n нельзя Используются довольно редко.. Язык С++ - занятие 619
Константные методы class abc { int i; void f() const { i++;// ошибка } … Нельзя менять поля Из f можно вызывать только константные методы Зачем? Ловим ошибки в программе Смысл метода становится понятнее (удобнее пользоваться) Техническая причина: к константным объектам можно применять только такие методы const time lunch_time(12,50); lunch_time.print(); // Только если у print - // константный метод Язык С++ - занятие 620
mutable class strange_shape { … }; double strange_shape::area() { … полчаса вычислений… } Чтобы не считать каждый раз: strange_shape() : saved_area(0) {…{… double strange_shape::area() { if (saved area == 0) { saved_area = … полчаса вычислений… } return saved_area; } Прием: кеширование (cache) Язык С++ - занятие 621
mutable - продолжение class strange_shape { … double strange_shape::area() const { … saved_area = …; // Изменение поля в константном методе??!! } … mutable int saved_area; }; mutable поле – можно менять в константных методах Язык С++ - занятие 622
volatile volatile int i; Смысл: i может меняться извне (например, из другого потока). (подробнее было нарисовано на доске). Язык С++ - занятие 623
Как сделать чтобы классом было удобнее пользоваться? Язык С++ - занятие 624
Будем определять класс complex class complex { double re, im; public: complex(double re_ = 0, double im_ = 0) : re(re_), im(im_) {} … Хотелось бы: - определения +, +=, *, *= - преобразования double -> complex и т.д. Язык С++ - занятие 625
friend Пример: синус complex c1, c2; c1 = sin(c2); complex sin(complex c) { … c.re … c.im … // ??? Другой вариант: sin, как метод c.sin(); // Неудобно.. friend class complex { double re, im; public: friend complex sin(complex c);... }; complex sin(complex c) { … c.re … c.im … // ОК Язык С++ - занятие 626
friend - продолжение friend объявление функции; Пишется внутри class Означает: Функция имеет доступ к private и protected полям и методам friend класс; Все методы класса – друзья class abc { … friend klm; }; // Все методы класса klm – // друзья класса abc. Не наследуется Не транзитивно. Язык С++ - занятие 627
Как задать свой оператор complex c1, c2, c3; c1 = c2 + c3; Можно определять функции и методы с именем: operator Например, operator+, operator
Ограничения при перегрузке операторов Только существующие операторы operator** - ошибка Стандартные приоритеты Один из операндов – class или struct (или enum или union) или ссылка на них Нет связи + и += и ++, или и т.д. Но см. boost::operators Язык С++ - занятие 729
Замечания Свои операторы – мне кажется, только если очевидно станет удобнее Бывают особые случаи, когда надо определить (следующие темы) Язык С++ - занятие 730
Как лучше задать operator+ ? 2 способа: Метод: class complex { complex operator+(complex c) { … } } c1 + c2 c1.operator+(c2); Обычная функция complex operator+(complex c1, complex c2) {… } c1 + c2 operator+(c1, c2); Правило: такие операторы, как правило, лучше определять, как обычную функцию Язык С++ - занятие 731
Оператор + для complex // Вариант 1 complex operator +(complex c1, complex c2) { complex res; res.re = c1.re + c2.re; res.im= c1.im + c2.im; return res; } В классе complex: friend complex operator+(complex c1, complex c2); // Вариант 2 complex operator+(complex c1, complex c2) { complex res(c1.re + c2.re, c1.im + c2.im); return res; } // Вариант 3 complex operator+(complex c1, complex c2) { return complex(c1.re + c2.re, c1.im + c2.im); } Язык С++ - занятие 732
Перегрузка операторов типа = c1 += c2; Лучше определять, как метод Напоминание: Все операции типа присваивания (=, +=, *=) возвращают ссылку на первый аргумент. n *= 10 // значение равно n, причем // это ссылка (n *= 10) + = 5 // тоже, что n = n*10 + 5; Язык С++ - занятие 733
Немного о строках Язык С++ - занятие 634
Строки ('старое' представление данных, в стиле С) char s[100]; cin >> s; // Ввести до пробела cin.getline(s, 100); // Ввести всю строку Строчки – это просто массивы символов. Конец строки обозначается символом '\0'. Т.е., например, если вы выполнили cin >> s; и ввели строчку abc, то у вас будет s[0] == a', s[1] == b', s[2] == c', s[3] == '\0 (а в остальных элементах массива s будет неизвестно что) Язык С++ - занятие 435
Задачи на 31 марта Язык С++ - занятие 636
Задачи на 31 марта Для деревьев описать функцию find_even, которая обходит дерево, и проверяет, есть ли в нем хотя бы одно четное число или нет и возвращает true или false. Желательно (не обязательно) с помощью исключений. (В смысле, с помощью исключений можно сразу выйти из нескольких рекурсивных вызовов, когда число найдено). 2. Пусть есть функция: void f(stack& s) { int* p = new int[1000]; cout
Задачи на 31 марта Описать функцию для сравнения двух строк. Пример вызова: char s1[100]; char s2[100]; cin.getline(s1, 100); cin.getline(s2, 100); int b = compare(s1, s2); // true, если строки равны (пожалуйста, не забывайте о const в параметрах) 4.* задача 3 без учета пробелов "ab ba" равно " a bba " 5.Определить для complex оператор += Обратите внимание, что, как обсуждалось выше, желательно, чтобы += можно было использовать в выражениях так: с1 = (с2 += с3); или так: (с1 += с2) += с3; и т.д. Язык С++ - занятие 638