Д.з. 1. Задача 3: список struct list { int val; list* next; list(int val_, list* next_) : val(val_), next(next_) {} }; void print(const list* p) { while.

Презентация:



Advertisements
Похожие презентации
Д.з. на 3 марта Язык С++ - занятие 51. Задача 4: top class stack { … int & top() { return stk[size -1 ]; } … }; // Пример вызова: stack s; … s.top() =
Advertisements

Д.з. на 17 марта Язык С++ - занятие 61. Задача 2: фигуры // shape - общий интерфейс для // фигур class shape { protected: int x, y; public: shape(int.
Д.з. 1. Задача 1: order // Вспомогательная функция: // Упорядочиваем два числа void order(int& x, int& y) { if (x > y) { int tmp = x; x = y; y = tmp;
Д.з Язык С++ - занятие 31. Задача 1: 1/1 + 1/3 + 1/5 … #include using namespace std; int main() { int n; cin >> n; double sum = 0;// Сумма for.
Д.з. на 31 марта Язык С++1Д.з. на 31 марта Язык С++1.
Д.з. на 3 марта Язык С++ - занятие 41Д.з. на 3 марта Язык С++ - занятие 41.
Обработка исключений Основы метапрограммированияОбработка исключений Основы метапрограммирования.
Лекция 10 ОбъектыЛекция 10 ОбъектыООП Инкапсуляция Возможность совместного хранения данных и кода для их обработки Наследование Возможность расширять существующие.
ООП Классы Данные отдельно, методы отдельно struct Node { Node* next; void* data; }; struct List { Node* first; int size; }; void* allocate() { … } void.
Наследование time time_with_sec В чем преимущества наследования? Наследование кода – поля, метод inc Появилось два класса с которыми точно можно обращаться.
Инструкции C++ Условная инструкция Формат: if (условие) оператор; else оператор; Пример: if (i!=0) { if (j) j++; if(k) k++; else if(p) k--; } else i--;
Статические поля класса Статические поля хранят данные, общие для всех элементов класса. Статическое поле существует в единственном экземпляре для всех.
C++ - занятие 2 1. Какие типы вы бы использовали? height // рост salary // зарплата за месяц (в рублях) grade// средний балл charshort longint unsigned.
Объектно-ориентированное программирование С++. Лекция 6 Карпов В.Э.
Основы информатики Классы Заикин Олег Сергеевич zaikin.all24.org
Учебный курс Объектно-ориентированный анализ и программирование Лекция 7 Методы как средство реализации операций Лекции читает кандидат технических наук.
Наследование Полиморфизм ВЫЗОВ КОНСТРУКТОРОВ И ДЕСТРУКТОРОВ ПРИ НАСЛЕДОВАНИИ.
Преобразования типов В языке C/C++ имеется несколько операций преобразования типов. Они используются в случае, если переменная одного типа должна рассматриваться.
Лекция 4. Введение в С++ Наследование, множественное наследование. Конструкторы, деструкторы. Виртуальные функции.
Лекция 8. Введение в ООП. Часть 1 Красс Александр СПбГУ ИТМО, 2008.
Транксрипт:

Д.з. 1

Задача 3: список struct list { int val; list* next; list(int val_, list* next_) : val(val_), next(next_) {} }; void print(const list* p) { while (p != nullptr) { cout val next; } } // Добавление list* newel = new list; newel->val = v; newel->next = first; first = newel; Или можно короче first = new list(v, first);

Задача 3: список (продолжение) void add(list* & p, int v) { p = new list(p, v); } // Пример вызова list* first = nullptr; for (int i = 0; i

Задача 1: равнобедренный треугольник class triang { protected: int x, y; // Координаты // середины основания int h, w; // Длина основания public: // и высота triang(int x_, int y_, int h_, int w_); virtual void draw() const; virtual ~rhomb() {} // Лучше так! }; // Вспомогательная функция, // рисует отрезок void draw_line(int x1, int y1, int x2, int y2) { cout

Задача 1: равнобедренный тругольник - продолжение class crossed_triang : public triang { public: crossed_ triang(int x_, int y_, int h_, int w_); void draw() const; }; crossed_ triang ::crossed_ triang( int x_, int y_, int h_, int w_) : triang(x_, y_, h_, w_) {} void crossed_ triang::draw() const { // "Унаследованное" изображение triang::draw(); // Еще линии draw_line(x + w/4, y, x + w/4, y + h/2); draw_line(x - w/4, y, x - w/4, y + h/2); } // Пример вызова: // Так мы проверим, что виртуальная // функция вызывается правильно rhomb* p = new crossed_ triang(30, 40, 10, 5); p->draw(); Типичные ошибки: Нет virtual

Еще про наследование 6

Когда используется то, что функция виртуальная? 1. Доступ через указатель time* p; if (…) p = new time(8, 30); else p = new time_with_sec(8, 30, 15); p->print(); // М.б. разные типы 2. Доступ через параметр-ссылку void my_print(time& t) { cout

Когда используется то, что функция виртуальная? (Продолжение) 4. Метод в базовом классе class time { … void my_print() { cout

Еще другими словами: как понимать слово virtual... Если нам дали класс, в котором есть виртуальная функция: class abc { virtual void f(); Это надо читать так: «… Функцию f вы можете переопределить и задать свой вариант…» 9

Если в документации написано virtual... = 0; class abc { virtual void f() = 0; Это надо читать так: «… Функцию f вы должны переопределить и задать свой вариант. …» 10

Можно ли конструктор обьявлять virtual? Нет! Когда нужна виртуальная функция? Код должен работать с разными обьектами … p->print(); // И для time // и для time_with_sec Для конструктора так не бывает: time t; p = new time(); Тип всегда известен (Есть понятие виртуальный конструктор – но это просто такой прием программирования) Техническая проблема – пока работает конструктор, ссылка на vtable еще не установлена Следствие: в конструкторе лучше не вызвать виртуальные функции 11

Можно ли деструктор обьявлять virtual? Да, и почти всегда надо! class shape {... }; class huge_shape : public shape { int* p; huge_shape() { p = new int [100000];... } ~huge_shape() { delete [] p; } }; // Если деструктор // не виртуальный: shape* p;... p = new huge_shape();... delete p; // Вызывается дестр-р // shape, а не huge_shape! // Утечка памяти! 12

Виртуальные деструкторы - продолжение А как правильно? В определении класса shape:... virtual ~shape() {} … Правило: Есть производные классы лучше определить в базовом классе деструктор (даже пустой), и объявить его виртуальным. или, другими, словами, Не имеет смысл определять виртуальный деструктор, только если для класса вообще не предполагается определять производные классы. 13

Д.з. про исключения 14

Задача 4: ошибка в конструкторе 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

Задача 4: еще вариант #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

Задача 5: исключения и утечка памяти // Исходная функция // … что-то делает со стеком … // Если стек пуст – исключение void f(stack& s) { int* p = new int[1000]; … что-то делаем с p … cout

Задача 5: еще вариант // Вариант 2: void f(stack& s) { int* p = new int[1000]; … что-то делаем с p … try { cout

Задача 5: еще вариант – "обернуть" p в класс! // Вариант 3: class wrapper { int* p; public: wrapper() { p = new int[1000]; } ~wrapper() { delete[] p; // Теперь delete точно выполниться } int* get() { return p; } }; void f(stack& s) { wrapper p; … что-то делаем с p.get() … cout

RAII Resource Acquisition Is Initialization Получение ресурса есть инициализация Пусть обязательно надо выполнить какое-то действие в конце функции или при выходе из блока (удалить память, закрыть файл, восстановить форму курсора и т.д.) Совет: завести вспомогательный класс выполнять это действие в деструкторе Преимущества: Получается короче Exception safe – действие будет выполнено даже при наличии исключений Замечание: Если действие – это освободить память то можно использовать стандартные классы (vector) или стандартныеумные указатели (scoped_ptr, shared_ptr)

Еще про исключения 21

Какие вообще бывают ошибки и как их обрабатывать? 1. Ошибка, которой в отлаженной программе быть не должно i = *p; // А если p == 0? assert(условие); #include … assert(p != 0); Если условие не выполнится –> сообщение Только в Debug версии 2. Ошибка, которую можно разумно обработать / исправить: Код завершения bool ok = f(); if (ok) 3. Ошибка, для которой разумная обработка – это завершение работы (в каком-то смысле): throw

Константы в классах 23

Пример, когда с const все не так просто 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) 24

mutable поля class strange_shape { … double strange_shape::area() const { … saved_area = …; // Изменение поля в константном методе??!! } … mutable int saved_area; }; mutable поле – можно менять в константных методах 25

Как сделать чтобы классом было удобнее пользоваться? 26

Будем определять класс complex class complex { double re, im; public: complex(double re_ = 0, double im_ = 0) : re(re_), im(im_) {} … Хотелось бы: - определения +, +=, *, *= - преобразования double -> complex и т.д. 27

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 … // ОК 28

friend - продолжение friend объявление функции; Пишется внутри class Означает: Функция имеет доступ к private и protected полям и методам friend класс; Все методы класса – друзья class abc { … friend klm; }; // Все методы класса klm – // друзья класса abc. Не наследуется Не транзитивно. 29

Как задать свой оператор complex c1, c2, c3; c1 = c2 + c3; Можно определять функции и методы с именем: operator Например, operator+, operator

Ограничения при перегрузке операторов Только существующие операторы operator** - ошибка Стандартные приоритеты Один из операндов – class или struct (или enum или union) или ссылка на них Нет связи + и += и ++, или и т.д. Но см. boost::operators 31

Замечания Свои операторы – мне кажется, только если очевидно станет удобнее Бывают особые случаи, когда надо определить (следующие темы) 32

Как лучше задать operator+ ? 2 способа: Метод: class complex { complex operator+(complex c) { … } } c1 + c2 c1.operator+(c2); Обычная функция complex operator+(complex c1, complex c2) {… } c1 + c2 operator+(c1, c2); Правило: такие операторы, как правило, лучше определять, как обычную функцию. 33

Оператор + для 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); } 34

Перегрузка операторов типа = c1 += c2; Лучше определять, как метод Напоминание: Все операции типа присваивания (=, +=, *=) возвращают ссылку на первый аргумент. n *= 10 // значение равно n, причем // это ссылка (n *= 10) + = 5 // тоже, что n = n*10 + 5; class complex { … complex& operator+= (complex c) { re += c.re; im += c.im; return *this; } В конце операторов типа = всегда пишут: return *this; 35

Как задать свои правила преобразования типов? 36

Преобразования типов Цель – писать: complex c = 3.5; c = 2.2; c += 3.1; Конструктор с 1 параметром задает преобразование типа complex(double re_ = 0, double im_ = 0) : re(re_), im(im_) {} // Примеры вызова c = complex(1.5); c = (complex)1.5; c = complex(1.5) 37

Неявные преобразования Преобразование типов иногда вызывается неявно: complex c = 1.5; complex c(1.5); c = 1.5; c = complex(1.5); Вызов функции: void f(complex c) { … } f(1.5); f(complex(1.5)) 38

Почему operator+ - не метод c1 = 1 + c;// Хотелось бы, чтобы так работало Если определим +, как обычную функцию c1 = operator+(1, c); // OK. Неявное преобразование, автоматически // преобразуется в // c1 = operator+(complex(1), c) Если определим +, как метод c1 = 1.operator+(c); // Ошибка. Не преобразуется в // c1 = complex(1).operator+(c); 39

Оператoр преобразования типа double x = (double)c; // Допустим, мы и так хотим // писать class complex { … operator double() const operator double() { return re;// Просто для }// примера.. }; operator имя-типа тип результата указывать не надо 40

Замечания про неявные прeобразования Замечания: д.б. однозначно (иначе будет ошибка компилятора) только одно (не подбираются цепочки преобразований) Точнее, одно определяемое + одно числовое complex c = 22; Когда применять? Мне кажется, только если очевидно, чтобы станет заметно удобнее. Компилятор не запутается, а вот вы можете 41

Д.з. 42

Д.з.-1 1.Для complex определить * и *= (Видимо, имеет смысл один из операторов определить через другой) 2. Определить структуру (или класс) «двоичное дерево». а. Ввести последовательность чисел, пока не будет введен 0, и создать из них упорядоченное двоичное дерево (дерево поиска). б. Напечатать все числа в вершинах дерева. 3. Определить класс rational (рациональное число) с полями числитель и знаменатель, и для него задать конструктор и преобразование в double: rational r(1,3); double x=r; // x = Задача 4 на следующем слайде 43

Д.з В реализацию стека добавить метод maxsin(), который считает максимум из синусов попарных произведений всех чисел на стеке. При этом давайте условно считать, что синус – это очень, очень долго работающая функция и заказчик просил вызывать ее как можно меньше. Поэтому заказчик просил: Производить вычисления только если вызывается max sin(), ничего не вычислять заранее. Запоминать результат и, если maxsin вызывается несколько раз подряд – просто возвращать запомненное значение. Еще начальник просил правильно расставить слова const и mutable Еще 0.5 балла, если при вычислении нового значения вы будете использовать последнее вычисленное ранее значение – хотя бы в некоторых случаях Замечания : Чтобы использовать sin надо в начале написать: #include Для простоты давайте считать, что функции top() у стека нет Если условие непонятно – пишите.. 44