MP2_Lec_OOP_02_03_ClassesAndObjects.ppt
- Количество слайдов: 52
Нижегородский государственный университет им. Н. И. Лобачевского Факультет вычислительной математики и кибернетики Методы программирования (2 семестр) Раздел 2. Лекция 02 Классы и объекты 1
Краткое содержание предыдущей серии q q q 2 Есть задачи, где идей структурного и модульного программирования недостаточно – сложные задачи. На помощь приходит объектно-ориентированный подход (технология). В основе подхода – объектно-ориентированная декомпозиция. 3 части – OOA, OOD, OOP. Основные принципы: абстракция, инкапсуляция, иерархия (наследование), полиморфизм.
Дальнейшее изучение Реализация основных идей объектного подхода средствами языка программирования C++. q Инкапсуляция в C++. q Наследование в C++. q Полиморфизм в C++. Итак, за дело! 3
Мы такие “бяки-буки“ И, фон Нейман нас прости, Мы плодим такие глюки, Вам вовек не разгрести. 4
Вместо вступления. С чего мы начинаем? Шаг 1 q Пусть мы выделили некоторую абстракцию в предметной области (пример: прямоугольник). q Может нужно что-то уточнить? Стороны прямоугольника параллельны осям координат. q 5
Вместо вступления. С чего мы начинаем? Шаг 2 q Мы определили, какими свойствами он характеризуется (x, y, a, b – вещественные числа). b q q 6 (x; y) a Что это было? Проектирование данных.
Вместо вступления. С чего мы начинаем? Шаг 3 q Мы определили, какие действия можно выполнять над прямоугольником, как они работают. • • • q q 7 Показать (Show). Скрыть (Hide). Подвинуть на dx, dy. Вычислить площадь. … Что это было? Проектирование операций. S = ab
Вместо вступления. С чего мы начинаем? Итог нашей деятельности: q Выделена абстракция, существенная для предметной области. q Определено, какими данными характеризуются все такие абстракции. q Определено, какие операции можно выполнять над такими абстракциями. Что мы получили? В первую очередь, мы получили определение нового типа данных – “прямоугольник”! 8
Типы данных Вспомним определение типа данных. Ранее было выяснено, что тип данных определяет: q Множество значений. q Объем оперативной памяти, необходимой для хранения данных. q Набор операций, применимых к значениям данного типа. q Способ интерпретации данных, находящихся в оперативной памяти. 9
Абстрактные типы данных Существуют стандартные типы данных в языке программирования C: int, float, double… q Существуют средства проектирования новых типов данных: массивы, перечисления, структуры… q Абстрактный тип данных (АТД) – тип данных, отсутствующий в машинной реализации и сконструированный средствами языка программирования. АТД = <Z; O>, где Z – множество значений; O – множество операций. q 10
Понятие класса_1 q 11 В языке программирования C существуют средства конструирования абстрактных типов данных. Однако, легко заметить, что множество значений и набор операций при этом никак не связаны друг с другом синтаксически! Связь присутствует только в голове программиста.
Понятие класса_2 q q 12 Язык C++ предоставляет средства реализации объектно-ориентированного подхода. Основа этих средств – понятие класса. Класс является абстрактным типом данных, определяемым пользователем, и представляет собой модель реального объекта в виде данных и функций для работы с ними.
Объявление класса_1 Итак, класс = данные + операции. Терминология: q Элементы данных принято называть полями (member fields). Поля реализуются способом, сильно напоминающим обычное объявление переменных. q Операции принято называть методами (member functions). Методы реализуются способом, сильно напоминающим обычное объявление функций. 13
Объявление класса_2 Простейший вариант: class <имя класса> { [private: ] <скрытые поля>; <скрытые методы>; public: <открытые поля>; <открытые методы>; }; ; завершает объявление класса! 14 Скрытая часть – детали специальные внутренней ключевые реализации слова Открытая часть – функциональность, предоставляемая пользователю класса (Вашему коллегепрограммисту)
Секции private и public q q q 15 private и public – спецификаторы доступа. Определяют 2 секции класса. То, что объявлено в секции private, является скрытым и доступно только внутри методов данного класса и недоступно извне. То, что объявлено в секции public, является интерфейсом класса и доступно везде. Секция private может отсутствовать. Секция public должна присутствовать всегда, иначе классом нельзя будет воспользоваться. Секции можно объявлять несколько раз, чередуя их произвольным образом.
Поля класса q q 16 Могут иметь любой тип, кроме типа этого же класса (но могут быть указателями или ссылками на этот класс). Могут быть описаны с модификатором const. При этом они инициализируются только один раз (с помощью конструктора при создании объекта) и не могут изменяться. Могут быть описаны с модификатором static (статические поля, об этом позже). Инициализация полей при описании не допускается.
Инкапсуляция Идеи инкапсуляции реализуются в C++ при помощи следующих конструкций языка: q class – позволяет объединить данные и операции над ними в рамках одной синтаксической структуры языка программирования. q секции public и private позволяют реализовать разграничения доступа, позволяя: • скрыть детали внутренней реализации. • организовать защиту данных. 17
Скрытие данных Часто встречающаяся рекомендация: скрывайте все Ваши данные, объявляя их в секции private! Что из этого следует? Если Вы хотите придерживаться этого тезиса, то Вам придется: q объявить все поля в секции private; q создать методы для доступа к этим полям; в противном случае, Вы просто не сможете с ними работать. Т. о. , Вам понадобится то, что на жаргоне называется “setters and getters” – методы для установки значений скрытых полей (Set. Something) и чтения значений (Get. Something). 18
Любую прогрессивную идею можно довести до уровня абсурда Не скрывайте все данные просто так. Скрывайте то, что действительно относится к деталям реализации! Иначе, кроме потери эффективности и загромождения бесполезными методами Вы ничего не добьетесь (моя частная точка зрения ). 19
Скрытие методов К q q 20 скрытию методов нужно подходить с умом. Рекомендации таковы: Скрывайте те методы, которые имеют отношение к деталям внутренней реализации. Скрывайте методы, которые с Вашей точки зрения не понадобятся пользователю разработанного Вами класса. Секция public – интерфейс Вашего класса. Чем меньше там написано, тем проще пользователю класса разобраться, как с ним работать. Создавайте те методы, которые нужны. Не создавайте 1000 методов только потому, что это красиво смотрится. Пользователь класса обязательно запутается в них.
Пример: класс “комплексное число”_1 Проектирование: q поля: re, im – действительная и мнимая часть. q методы: • • 21 Set. Re, Get. Re, Set. Im, Get. Im. Арифметические. Вывод на экран (печать себя). Специальные (конструктор, деструктор – рассмотрим позже).
Пример: класс “комплексное число”_2 class TComplex { private: int Re, Im; // data public: //Set & Get int Get. Re() { return (Re); void Set. Re(int _Re) { Re = int Get. Im() { return (Im); void Set. Im(int _Im) { Im = // Arithmetic methods TComplex Add(TComplex C); // User interface void Print(); }; 22 } _Re; } } _Im; }
Анализ примера_1 q q q 23 Данные скрыты. Созданы методы Set и Get. У этих методов в объявлении класса приведена реализация. В этом случае методы приобретают свойство “inline”, т. е. становятся встраиваемыми. Таким образом, компилятор в каждом месте, где происходит вызов метода, вместо вызова функции поставляет целиком ее тело. Рекомендация: имеет смысл “инлайнить” методы, реализация которых состоит из очень небольшого количества команд. Программный код разбухнет незначительно, зато получим большой выигрыш в скорости работы программы.
Анализ примера_2 q q 1) 2) 24 Объявлено 2 метода (для сложения и вывода значения на консоль). Обратим внимание на метод сложения: TComplex Add(TComplex C); Этот метод принимает 1 параметр C типа TComplex. Семантика работы метода: берет объект, к которому его применили, и к нему прибавляет комплексное число C. Далее 2 варианта: Результат записывается в исходный объект и его копия возвращается как результат. По смыслу получили аналог операции +=. Формируется новый объект, выполняется операция сложения, новый объект возвращается как результат. По смыслу получили аналог операции +.
Анализ примера_3 q q q 25 А какой из вариантов имеет место на самом деле? А это зависит от нас. Вернее, от того, как мы напишем реализацию метода. Вопрос: где ее писать? Ответ: возможны 2 варианта: 1) непосредственно в объявлении класса – получим встроенный метод; 2) отдельно.
Объявление и реализация. Шаг 1 q Создаем файл complex. h и помещаем туда объявление класса. class TComplex { private: int Re, Im; // data public: //Set & Get int Get. Re() { return (Re); void Set. Re(int _Re) { Re = int Get. Im() { return (Im); void Set. Im(int _Im) { Im = // Arithmetic methods TComplex Add(TComplex C); // User interface void Print(); }; 26 } _Re; } } _Im; }
Объявление и реализация. Шаг 2 q Создаем файл complex. cpp и помещаем туда реализацию методов. #include “complex. h” TComplex: : Add(TComplex C) { … // реализация } void TComplex: : Print() { … // реализация } Общий вид: <Тип результата> <Имя класса>: : <Имя метода>(<Список параметров>) { <реализация> } 27
Объявление и реализация. Шаг 2. Варианты реализации метода Add // Аналог операции += TComplex: : Add(TComplex C) { Re += C. Get. Re(); Im += C. Get. Im(); return *this; } // Аналог операции + TComplex: : Add(TComplex C) { TComplex temp; temp. Set. Re(Re + C. Get. Re()); temp. Set. Im(Im + C. Get. Im()); return temp; } // На самом деле реализуем что-то одно. 28
Разные соображения q Вернемся к заголовку метода Add: TComplex Add(TComplex C); q Элементы оптимизации: копирование параметра – долго, будем передавать его по ссылке: TComplex Add(TComplex &C); q Проблема: операции + и += не должны изменять правую часть! Решение: TComplex Add(const TComplex &C); q Посмотрим на метод Print: void Print(); q Метод Print не должен менять поля класса. По смыслу он “константный”. void Print() const; 29
Константные методы Константный метод: q объявляется с ключевым словом const после списка параметров; q не может изменять значения полей класса; q может вызывать только константные методы; q может вызываться для любых (не только константных) объектов. 30
Пример: класс “комплексное число” class TComplex { private: int Re, Im; // data public: //Set & Get int Get. Re() const { return (Re); }; void Set. Re(int _Re) { Re = _Re; }; int Get. Im() const { return (Im); }; void Set. Im(int _Im) { Im = _Im; }; // Arithmetic methods TComplex Add(const TComplex &C); // User interface void Print() const; }; 31
Создание объектов До сих пор мы описывали тип данных. А как создавать переменные этого типа? q Переменные типа “класс” называются объектами или экземплярами. Логичная терминология, в отличие от Object Pascal. Синтаксис объявления стандартный: q Статическое создание объекта: <Имя класса> <Имя объекта>; q Динамическое создание объекта: <Имя класса> *<Имя объекта> = new <Имя класса>(); q 32
Примеры статического и динамического создания объектов Статическое создание объектов: TComplex A, B; q Динамическое создание объекта: TComplex *A = new TComplex(); В дальнейшем мы увидим, что синтаксис может быть и более сложным (раздел “конструкторы”). q В случае статического/динамического создания время жизни и область действия объекта определяется общими правилами, существующими в C++ для времени жизни и области действия переменных. 33
Доступ к данным и методам объектов q В случае статического создания объекта доступ к его полям и методам осуществляется посредством точки: TComplex A(); A. Set. Re(1); A. Set. Im(5); A. Print(); A. Re = 8; //Ошибка компиляции! Поле Re скрыто q В случае динамического создания объекта доступ к его полям и методам осуществляется посредством стрелки: TComplex *A = new TComplex(); A->Set. Re(1); A->Set. Im(5); A->Print(); A->Re = 8; //Ошибка компиляции! Поле Re скрыто 34
Создание объектов. Взгляд изнутри TComplex A, B; A Re Im B Re Im ОЗУ Реализация методов A. Set. Re(1); A. Set. Im(2); B. Set. Re(2); B. Set. Im(3); 35 A 1 2 B 2 3 Реализация методов
Объекты и методы класса. Как объект “добирается” до методов своего класса Итак, мы разобрались, что представляет собой объект – это обычная переменная сложного (абстрактного) типа данных (класс), наряду с данными содержащая ссылку на методы своего класса. Отметим некоторые, хотя и очевидные, но, все-таки, важные моменты: q Каждый объект имеет собственные данные в соответствии с тем, как описан его класс. q Каждый объект имеет ссылку на методы класса, реализация которых существует в памяти в единственном экземпляре. 36
Как метод “добирается” до данных объекта. Указатель this _1 Рассмотрим метод Set. Re совместно с примером его использования. Реализация: void Set. Re(int _Re) { Re = _Re; } q Использование: A. Set. Re(1); q 37 Вопрос: как это работает? Смотрим реализацию: какому-то Re присваивается переданное в функцию значение. А как компилятор узнает, у какого именно объекта нужно менять Re?
Как метод “добирается” до данных объекта. Указатель this _2 Реализация: void Set. Re(int _Re) { Re = _Re; } Использование: A. Set. Re(1); Ответ: q на самом деле ни один метод класса не вызывается сам по себе; q каждый метод вызывается для конкретного объекта (A в примере); q каждый метод класса обязательно получает 1 неявный параметр, указатель на объект, для которого был вызван! (есть исключение – статические методы, об этом позже); q внутри метода указатель доступен под именем this. 38
Как метод “добирается” до данных объекта. Указатель this _3. Пример Реализация 1: void Set. Re(int _Re) { Re = _Re; } Реализация 2: void Set. Re(int _Re) { this->Re = _Re; } РЕАЛИЗАЦИЯ 1 ≡ РЕАЛИЗАЦИЯ 2 39
Как метод “добирается” до данных объекта. Указатель this _4. Пример Использование: A. Set. Re(1); B. Set. Re(2); ОЗУ A B void Set. Re(int _Re) { this->Re = _Re; } 40 A Re Im B Re Im
Использование указателя this Указатель this неявно используется компилятором. Вопрос: когда возникает необходимость его использования? // Аналог операции += q TComplex: : Add(const TComplex &C) { Re += C. Get. Re(); Im += C. Get. Im(); return *this; } 41
Некоторые “хитрости” и “тонкости” в статическом и динамическом создании объектов внутри методов Мыслим мы машинным кодом, Это высший пилотаж. Всех поздравит с Новым Годом Самый новый вирус наш. q q 42 Вопрос 1: что произойдет, сели мы будем писать возвращаемый результат в методе со значком & ? Вопрос 2: как это будет взаимодействовать со статическим и динамическим созданием объектов внутри методов?
Профессиональные программисты о языке C/C++ When the non-C programmers around here need to do a little C work, I just tell them to add asterisks and ampersands until it works or come get me © Charles Patterson источник: http: //lye. upnet. ru Проблема в том, что многие так и поступают. Иногда “метод научного тыка” вдруг перестает работать в самый неподходящий момент. Как же обойтись без него? НУЖНО ПОНИМАТЬ, КАК ЭТО РАБОТАЕТ! 43
Энциклопедия ошибок в работе со ссылками в заголовке метода. Глюк № 1 void TComplex: : Make. Same. As(const TComplex &C) void TComplex: : Make. Same. As(TComplex &C) { { int _re = re, _im = im; re = C. Get. Re(); im = C. Get. Im(); Решение C. Set. Re(_re); C. Set. Im(_im); } … Тот, кто реализовывал void main(void) { метод, ошибся и вместо копирования написал обмен TComplex A(), B(); Тот, кто A. Print(); B. Print(); значениями. пользовался классом, узнает A. Make. Same. As(B); этом не скоро! A. Print(); B. Print(); об “Сюрприз будет” } 44
Если бы люди строили дома так же, как программисты пишут свои программы, то первый же залетевший дятел разрушил бы всю цивилизацию. Народная мудрость. 45
Энциклопедия ошибок в работе со ссылками в заголовке метода. Глюк № 2 // Аналог операции + TComplex& TComplex: : Add(const TComplex &C) {{ TComplex temp(); temp. Set. Re(Re + C. Get. Re()); temp. Set. Im(Im + C. Get. Im()); Решение temp. Set. Im(Im + C. Get. Im()); return temp; } } Как это работает? 1. Создается temp. 2. Ему присваиваются значения. 3. Возвращается адрес локальной переменной temp. 4. temp уничтожается! 46
If debugging is the art of removing bugs, then programming must be the art of putting them in. Edsger W. Dijkstra 47
Энциклопедия ошибок в работе со ссылками в заголовке метода. Глюк № 3 // Аналог операции + TComplex& TComplex: : Add(const TComplex &C) { TComplex* temp = new TComplex(); temp->Set. Re(Re + C. Get. Re()); temp->Set. Im(Im + C. Get. Im()); return *temp; } Как это работает? 1. Создается динамически temp. 2. Ему присваиваются значения. 3. Возвращается адрес динамически созданной переменной temp. 4. temp не уничтожается! А если это происходит в цикле? 48
Энциклопедия ошибок в работе со ссылками в заголовке метода. Глюк № 3 // Аналог операции + TComplex& TComplex: : Add(const TComplex &C) { TComplex* temp = new TComplex(); temp->Set. Re(Re + C. Get. Re()); temp->Set. Im(Im + C. Get. Im()); Решение: возвращаем return *temp; TComplex } Единственный способ освободить память: TComplex A(), B(), C(); TComplex *p; C = A. Add(B); // потеря памяти С = *(p = &A. Add(B)); delete p; // Ужас! 49
Выводы. Статическое и динамическое создание объектов внутри методов. Ссылки в заголовках методов q q q 50 Если метод не должен изменять полей класса, его нужно объявить с ключевым словом const. Если передаваемые параметры не должны изменяться, нужно передавать их либо по значению, либо как константную ссылку. Не рекомендуется возвращать в качестве результата работы метода ссылку, кроме как в тех случаях, когда без этого нельзя обойтись.
Следующая тема q 51 Создание объектов, конструкторы и деструкторы.
Павловская Т. А. C/C++ Программирование на языке высокого уровня. q Ален И. Голуб. Правила программирования на C/C++. Из книг заимствованы различные идеи, элементы текста. q http: //lye. upnet. ru q http: //xaxa. h 1. ru/jokes. html q 52
MP2_Lec_OOP_02_03_ClassesAndObjects.ppt