Языки программирования — Осенний семестр; Михалкович С.С.; 2008 — различия между версиями
Admin (обсуждение | вклад) (→Лекция 13 (02.12.08)) |
Juliet (обсуждение | вклад) |
||
(не показано 38 промежуточных версий 8 участников) | |||
Строка 61: | Строка 61: | ||
Статические конструкторы в PascalABC.NET, Java. | Статические конструкторы в PascalABC.NET, Java. | ||
+ | |||
Класс Динамический массив (PascalABC.NET). | Класс Динамический массив (PascalABC.NET). | ||
Строка 68: | Строка 69: | ||
Понятие итератора. Защита доступа. Интерфейс итератора. Итерация по списку с помощью итератора. Итерация с помощью foreach. | Понятие итератора. Защита доступа. Интерфейс итератора. Итерация по списку с помощью итератора. Итерация с помощью foreach. | ||
+ | |||
Класс Множество на PascalABC.NET. Реализация с помощью списков и с помощью бинарных деревьев поиска. | Класс Множество на PascalABC.NET. Реализация с помощью списков и с помощью бинарных деревьев поиска. | ||
Строка 137: | Строка 139: | ||
Перегрузка операций в PascalABC.NET (на примере класса Frac). | Перегрузка операций в PascalABC.NET (на примере класса Frac). | ||
+ | |||
Агрегация класов и подобъекты (C++). | Агрегация класов и подобъекты (C++). | ||
Строка 145: | Строка 148: | ||
Порядок вызова конструкторов и деструкторов. | Порядок вызова конструкторов и деструкторов. | ||
− | Клонирование и присваивание объектов в языках со ссылочной моделью хранения объектов (на примере PascalABC.NET) | + | Клонирование и присваивание объектов в языках со ссылочной моделью хранения объектов (на примере PascalABC.NET) |
==Лекция 10 (11.11.08)== | ==Лекция 10 (11.11.08)== | ||
Строка 170: | Строка 173: | ||
Напишем классы Queue<T> и CountingQueue<T> с использованием включения (PABC.NET). Понятие делегирования. Достоинства наследования. Пример плохого использования наследования: | Напишем классы Queue<T> и CountingQueue<T> с использованием включения (PABC.NET). Понятие делегирования. Достоинства наследования. Пример плохого использования наследования: | ||
+ | <source lang="delphi"> | ||
Circle = class( Point ); | Circle = class( Point ); | ||
+ | </source> | ||
В обоих классах есть реализация конструктора и процедуры Draw. | В обоих классах есть реализация конструктора и процедуры Draw. | ||
Строка 179: | Строка 184: | ||
Наследование и выявление общего предка. | Наследование и выявление общего предка. | ||
− | Даны UML-нотации классов Student и Teacher. Заметим, что у них есть общие поля и методы. Решаем вынести их в базовый класс Person. Реализовали класс Teacher на Java, в т. ч. конструктор c super(...). | + | Даны UML-нотации классов <tt>Student</tt> и <tt>Teacher</tt>. Заметим, что у них есть общие поля и методы. Решаем вынести их в базовый класс Person. Реализовали класс Teacher на Java, в т. ч. конструктор c super(...). |
− | Вид доступа protected | + | <h3>Вид доступа protected</h3> |
Что значит protected. protected нарушает инкапсуляцию. Пример наследования Square от Rectangle на PABC.NET. Приходим к выводу, что иногда необходимо обращаться к полям и в этом случае следует использовать protected. | Что значит protected. protected нарушает инкапсуляцию. Пример наследования Square от Rectangle на PABC.NET. Приходим к выводу, что иногда необходимо обращаться к полям и в этом случае следует использовать protected. | ||
− | Класс Object — предок всех классов | + | <h3>Класс Object — предок всех классов</h3> |
Дан интерфейс класса Object в Java и .NET (не полный - из 4 и 3 методов соответственно). | Дан интерфейс класса Object в Java и .NET (не полный - из 4 и 3 методов соответственно). | ||
Строка 190: | Строка 195: | ||
Описаны переменные s (Student) и p (Person). Пытаемся одному присвоить другое. Делаем вывод, что переменная производного класса может быть неявно преобразована к типу базового класса, но не наоборот. Рассматриваем случай явного приведения типов. Вводятся понятия UpCast и DownCast. | Описаны переменные s (Student) и p (Person). Пытаемся одному присвоить другое. Делаем вывод, что переменная производного класса может быть неявно преобразована к типу базового класса, но не наоборот. Рассматриваем случай явного приведения типов. Вводятся понятия UpCast и DownCast. | ||
− | Операции is и as (PABC.NET) и instanceof (Java) | + | <h3>Операции is и as (PABC.NET) и instanceof (Java)</h3> |
Определения статического и динамического типа переменной. | Определения статического и динамического типа переменной. | ||
− | + | <h4>Два варианта использования операций is и as</h4> | |
− | 1. | + | '''Вариант 1.''' |
+ | ''PascalABC.NET'' | ||
+ | <source lang="delphi"> | ||
if p is Student then // не точное совпадение типов, а то, является ли p разновидностью Student | if p is Student then // не точное совпадение типов, а то, является ли p разновидностью Student | ||
Student(p).ChangeCourse(4); | Student(p).ChangeCourse(4); | ||
+ | </source> | ||
− | if( p instanceof Student ) | + | ''Java'' |
+ | <source lang="java"> | ||
+ | if (p instanceof Student) | ||
((Student)p).changeCourse(4); | ((Student)p).changeCourse(4); | ||
+ | </source> | ||
− | 2. | + | '''Вариант 2.''' |
− | + | ''PascalABC.NET'' | |
+ | <source lang="delphi"> | ||
+ | var s: Student := p as Student; | ||
+ | </source> | ||
− | + | ''Java'' | |
− | + | <source lang="java"> | |
− | + | Student st = (Student) p; | |
− | + | </source> | |
− | |||
− | |||
− | |||
− | |||
− | |||
==Лекция 12 (25.11.08)== | ==Лекция 12 (25.11.08)== | ||
− | Наследование в С++ | + | <h3>Наследование в С++</h3> |
Вспомним наш излюбленный пример наследования на примере классов Person и Student. В предположении, что класс Person уже описан, опишем на С++ класс Student. Одно из полей Student типа char*. Понятие публичного наследования. Особенности вызова конструктора (деструктора) предка в конструкторе (деструкторе) потомка. | Вспомним наш излюбленный пример наследования на примере классов Person и Student. В предположении, что класс Person уже описан, опишем на С++ класс Student. Одно из полей Student типа char*. Понятие публичного наследования. Особенности вызова конструктора (деструктора) предка в конструкторе (деструкторе) потомка. | ||
Пример использования char* надуман! Не пытайтесь повторить это дома! | Пример использования char* надуман! Не пытайтесь повторить это дома! | ||
− | Совместимость по присваиванию и преобразование типов в иерархии Предок–Потомок | + | <h4>Совместимость по присваиванию и преобразование типов в иерархии Предок–Потомок</h4> |
Описали переменные p (Person) и s (Student). Они хранятся на стеке (размерная модель). Пытаемся присвоить одному другое. Делаем вывод об отбрасывании дополнительных полей производного класса при присваивании. | Описали переменные p (Person) и s (Student). Они хранятся на стеке (размерная модель). Пытаемся присвоить одному другое. Делаем вывод об отбрасывании дополнительных полей производного класса при присваивании. | ||
− | Совместимость по присваиванию для типов указателей и ссылок | + | <h4>Совместимость по присваиванию для типов указателей и ссылок</h4> |
Присвоение указателям (ссылкам): UpCast - неявно, DownCast - только явно (с помощью операции static_cast). | Присвоение указателям (ссылкам): UpCast - неявно, DownCast - только явно (с помощью операции static_cast). | ||
Конструктор копии и оператор присваивания для класса Student. | Конструктор копии и оператор присваивания для класса Student. | ||
− | Полиморфизм и виртуальные | + | <h3>Полиморфизм и виртуальные методы</h3> |
(Java) Пусть в классах Person и Student есть метод print(). | (Java) Пусть в классах Person и Student есть метод print(). | ||
− | + | Рассмотрим код: | |
− | + | <source lang="delphi"> | |
+ | Person p = new Student(...); | ||
+ | p.print(); | ||
+ | </source> | ||
− | + | Метод print() какого класса будет вызван? В Java - класса Student, а в OP и C++ (по умолчанию) - класса Person. | |
− | + | Понятия раннего и позднего связывания. | |
Реализация позднего связывания на OP. | Реализация позднего связывания на OP. | ||
Строка 252: | Строка 264: | ||
==Лекция 13 (02.12.08)== | ==Лекция 13 (02.12.08)== | ||
− | <h3>Виртуальные | + | <h3>Виртуальные методы как блоки для замены кода</h3> |
1. При разработке базового класса надо думать о будущем коде. | 1. При разработке базового класса надо думать о будущем коде. | ||
Строка 258: | Строка 270: | ||
Определение полиморфного объекта (объекта, обладающего полиморфным поведением), полиморфной подпрогораммы. | Определение полиморфного объекта (объекта, обладающего полиморфным поведением), полиморфной подпрогораммы. | ||
− | + | <h3>Виртуальные методы в классе Object</h3> | |
В С++ нет класса, базового для всей иерархии классов. | В С++ нет класса, базового для всей иерархии классов. | ||
Рассмотрели пример переопределения метода ToString класса Object на PABC.NET. | Рассмотрели пример переопределения метода ToString класса Object на PABC.NET. | ||
− | + | <h3>Цепочки виртуальности и ее разрыв</h3> | |
Определение цепочки виртуальности. | Определение цепочки виртуальности. | ||
Строка 271: | Строка 283: | ||
Идентификатор reintroduce (NET). | Идентификатор reintroduce (NET). | ||
− | + | <h3>С++ - виртуальные деструкторы</h3> | |
Конструкторы в С++ виртуальными быть не могут, а деструкторы могут. | Конструкторы в С++ виртуальными быть не могут, а деструкторы могут. | ||
Правило: Если в классе есть хотя бы одна виртуальная функция или нет, но в подклассах они могут появиться, то деструктор этого класс следует сделать виртуальным. | Правило: Если в классе есть хотя бы одна виртуальная функция или нет, но в подклассах они могут появиться, то деструктор этого класс следует сделать виртуальным. | ||
Правило: Если деструктор базового класса виртуальный, то сгенерированный деструктор также будет виртуальным. | Правило: Если деструктор базового класса виртуальный, то сгенерированный деструктор также будет виртуальным. | ||
− | + | <h3>Полиморфные контейнеры</h3> | |
− | + | <h4>Полиморфный контейнер в .NET</h4> | |
Рассмотрели пример на PascalABC.NET в котором <code>Shape</code> - это предок классов геометрических фигур. Хотим написать методы Draw и Hide. Вместо этого (вместе с этим) дали определение абстрактного метода и абстрактного класса. Идем дальше. Реализовали класс Shape. В глаза бросается метод MoveTo, который вызывает два абстрактных виртуальных метода (Hide и Draw). Интересно, он вообще работает... Ответ: да, работает, ведь перед Hide и Draw неявно стоит self - ссылка, - как раз то, что надо для виртуальных методов. | Рассмотрели пример на PascalABC.NET в котором <code>Shape</code> - это предок классов геометрических фигур. Хотим написать методы Draw и Hide. Вместо этого (вместе с этим) дали определение абстрактного метода и абстрактного класса. Идем дальше. Реализовали класс Shape. В глаза бросается метод MoveTo, который вызывает два абстрактных виртуальных метода (Hide и Draw). Интересно, он вообще работает... Ответ: да, работает, ведь перед Hide и Draw неявно стоит self - ссылка, - как раз то, что надо для виртуальных методов. | ||
Заполнили List<Shape> объектами производного от Shape класса, в одном цикле foreach нарисовали их, в другом переместили. | Заполнили List<Shape> объектами производного от Shape класса, в одном цикле foreach нарисовали их, в другом переместили. | ||
Строка 284: | Строка 296: | ||
Если класс абстрактный, то объекты этого класса создавать нельзя. | Если класс абстрактный, то объекты этого класса создавать нельзя. | ||
− | + | <h4>Полиморфный контейнер на С++</h4> | |
Написали тот же класс Shape на С++. Здесь Hide и Draw называются уже чисто виртуальными ( =0 ). Затем заполнили vector<Shape*> объектами производного класса, в цикле (с итератором) нарисовали их, а потом еще и переметили на заданный ветктор. Полиморфное клонирование в полиморфном контейнере. | Написали тот же класс Shape на С++. Здесь Hide и Draw называются уже чисто виртуальными ( =0 ). Затем заполнили vector<Shape*> объектами производного класса, в цикле (с итератором) нарисовали их, а потом еще и переметили на заданный ветктор. Полиморфное клонирование в полиморфном контейнере. | ||
− | + | <h3>Система RTTI в С++</h3> | |
Если в классе не определено ни одной виртуальной функции, то аналогии is и as ввести нельзя. Аналогии операций is и as. | Если в классе не определено ни одной виртуальной функции, то аналогии is и as ввести нельзя. Аналогии операций is и as. | ||
Строка 311: | Строка 323: | ||
3-ий способ: Покрасить все треугольники, но не производные классы, используя только полиморфизм. | 3-ий способ: Покрасить все треугольники, но не производные классы, используя только полиморфизм. | ||
− | + | <h3>Таблица виртуальных методов - внутренний механизм реализации полиморфизма</h3> | |
Рассмотрели иерархию классов (A ◅— B ◅— C) с не виртуальными и виртуальными методами и с разрывом цепочки виртуальности. Объявили переменную p: | Рассмотрели иерархию классов (A ◅— B ◅— C) с не виртуальными и виртуальными методами и с разрывом цепочки виртуальности. Объявили переменную p: | ||
− | var p: A; | + | var p: A; |
инициализировали: | инициализировали: | ||
− | p := new A; | + | p := new A; |
Затем последовательно присвоили p ссылки на B и на C и смотрели что происходит в памяти после каждого оператора присваивания. | Затем последовательно присвоили p ссылки на B и на C и смотрели что происходит в памяти после каждого оператора присваивания. | ||
Введены понятия VMT и vptr. Накладные расходы на вызов виртуальных методов (по памяти и по времени). | Введены понятия VMT и vptr. Накладные расходы на вызов виртуальных методов (по памяти и по времени). | ||
− | ===Интерфейсы (.NET и | + | <h3>Интерфейсы (.NET и Java)</h3> |
+ | |||
+ | Определение интерфейса. Описание основных интерфейсов. Пример объявления класса, поддерживающего некоторые интерфейсы. | ||
+ | |||
+ | ==Лекция 15 (16.12.08)== | ||
+ | |||
+ | Пример реализации интерфейса в Java (класс Student реализует ICloneable). | ||
+ | |||
+ | <h3>Интерфейсы, совместимость по присваиванию, преобразование типов, цепочки виртуальности</h3> | ||
+ | |||
+ | (все последующие примеры в .NET) | ||
+ | |||
+ | Пример реализации интерфейса (класс Student реализует ICloneable). Описание переменной типа интерфейс. Правила присваивания ей объектов различных классов. Полиморфный контейнер объектов, поддерживающих интерфейс ICloneable. Цепочка виртуальности, задаваемая интерфейсом. | ||
+ | |||
+ | <h3>UpCast, DownCast и CrossCast для интерфейсов</h3> | ||
+ | |||
+ | Написали два интерфейса и реализующий их класс. | ||
+ | <source lang="delphi"> | ||
+ | var im1: IMy1; | ||
+ | im2: IMy2; | ||
+ | m : MyClass; | ||
+ | begin | ||
+ | im1 := m; // UpCast - неявно | ||
+ | m := MyClass(im2); // DownCast - только явно | ||
+ | im2 := IMy2(im1); // CrossCast - только явно | ||
+ | end;</source> | ||
+ | |||
+ | <h3>Наследование интерфейса и наследование реализации</h3> | ||
+ | |||
+ | Понятия наследования интерфейса и наследования реализации. В каких случаях предпочтительнее наследование интерфейса, а в каких наследование реализации? Пример наследования от многих классов (как бы он мог выглядеть в .NET), проблемы такого наследования и решение проблем с помощью интерфейсов. | ||
+ | |||
+ | <h3>Принцип подстановки</h3> | ||
+ | |||
+ | Во всех ситуациях, в которых может использоваться объект базового класса, можно подставить объект производного класса без потери функциональности. | ||
+ | |||
+ | Принцип подстановки выполняется не всегда. Например, при наследовании с ограничением (круг от эллипса или вектор от матрицы) принцип подстановки не выполняется. | ||
+ | |||
+ | Принцип подстановки всегда выполняется если используется полиморфизм, а также при наследовании интерфейса. | ||
+ | |||
+ | <h3>Стандартные интерфейсы .NET</h3> | ||
+ | |||
+ | Пример 1. Интерфейс IComparable и его использование. | ||
+ | |||
+ | Пример 2. Интерфейс IComparer и его использование. | ||
+ | |||
+ | ==Лекция 16 (23.12.08)== | ||
+ | |||
+ | <h3>Исключения</h3> | ||
+ | |||
+ | <h4>Исключения в PABC.NET</h4> | ||
+ | |||
+ | Задача 1. Есть набор файлов текстового формата. Эти файлы содержат целые числа, разделенные пробелами и символами перехода на новую строку. Пользователь вводит имя файла. Программа выдает сумму чисел в файле. | ||
+ | |||
+ | Решение. | ||
+ | Начнем с самой вложенной функции. | ||
+ | <source lang="delphi"> | ||
+ | function EvalSum( data: Text ): integer; | ||
+ | begin | ||
+ | Result := 0; | ||
+ | while not SeekOEof(data) do | ||
+ | begin | ||
+ | var a: integer; | ||
+ | Read( data, a ); // Проблема 2 | ||
+ | Result += a; | ||
+ | end; | ||
+ | end;</source> | ||
+ | Функция обработки файла | ||
+ | <source lang="delphi"> | ||
+ | function ProcessFile( fName: string ):integer; | ||
+ | begin | ||
+ | var f: text; | ||
+ | Assign( f, fName ); | ||
+ | Reset(f); // Проблема 1 | ||
+ | Result := EvalSum(f); | ||
+ | Close(f); | ||
+ | end;</source> | ||
+ | Далее напишем бесконечный цикл, в котором будем спрашивать у пользователя имя файла и обрабатывать его. | ||
+ | Итого: имеем две проблемы: | ||
+ | |||
+ | проблема 1: Файл может не существовать. | ||
+ | проблема 2: В файле могут быть неправильные данные. | ||
+ | |||
+ | Решение 1 проблемы 1. Можно поставить перед Reset(f) условный оператор | ||
+ | <pre>Если файл существует | ||
+ | открой его на чтение</pre> | ||
+ | Сейчас, когда мы работаем в многозадачной среде может быть такое, что кто-то удалил файл после выполнения if'а, но перед Reset. | ||
+ | Это плохое решение. | ||
+ | |||
+ | Решение 1 проблемы 2. Можно считывать не integer, а string и пытаться преобразовать к интам, Но проблема вот в чем: если мы не смогли преобразовать, придется написать | ||
+ | <source lang="delphi">Writeln('Ошибка');</source> | ||
+ | А что если EvalSum используется в оконном приложении? Куда выведется результат? В воздух? | ||
+ | |||
+ | Решение 2 проблемы 2. Пусть функция возвращает код ошибки. | ||
+ | Для этого были придуманы исключения. | ||
+ | |||
+ | В коде с комментарием Проблема PABC.NET генерирует исключение. | ||
+ | <source lang="delphi"> | ||
+ | while true do | ||
+ | begin | ||
+ | var fName : string; | ||
+ | Realdn( fName ); | ||
+ | try | ||
+ | var sum := ProcessFile(fName); | ||
+ | writeln(sum); | ||
+ | except | ||
+ | Writeln('Ошибка'); // Хотелось бы поконкретней | ||
+ | end; | ||
+ | end;</source> | ||
+ | |||
+ | Понятия try-блока и секции обработки исключений. | ||
+ | <source lang="delphi"> | ||
+ | try | ||
+ | var sum := ProcessFile(fName); | ||
+ | writeln(sum); | ||
+ | except | ||
+ | on System.IO.FileNotFoundException do | ||
+ | Writeln('Файл отсутствует'); | ||
+ | |||
+ | on System.FormatException do | ||
+ | Writeln('Неверный формат файла'); | ||
+ | end;</source> | ||
+ | |||
+ | |||
+ | '''То,_что_надо_отвечать_на_экзамене_на_вопросы,_касательные_исключений''' | ||
+ | |||
+ | Генерация исключения прерывает нормальное выполнение подрограммы и приводит к раскрутке стека вызовов вплоть до обнаружения соответствующего try-блока. | ||
+ | <source lang="delphi"> | ||
+ | try | ||
+ | ... | ||
+ | except | ||
+ | on ... do ... | ||
+ | on ... do ... | ||
+ | |||
+ | ... | ||
+ | |||
+ | else | ||
+ | ... | ||
+ | end;</source> | ||
+ | |||
+ | В таком try-блоке будут обработаны все исключения без исключения(c). | ||
+ | |||
+ | Закрыть файл мы можем только там, где открыли. | ||
+ | <source lang="delphi"> | ||
+ | function ProcessFile( fName: string ):integer; | ||
+ | begin | ||
+ | var f: text; | ||
+ | Assign( f, fName ); | ||
+ | Reset(f); | ||
+ | try | ||
+ | Result := EvalSum(f); | ||
+ | finally | ||
+ | Close(f); // действия в блоке finally будут выполнены не зависимо от исключения | ||
+ | end; | ||
+ | end;</source> | ||
+ | |||
+ | Задача 2. Модифицируем задачу 1. Предположим все целые числа разделены двоеточием. | ||
+ | В этом случае все содержимое файла будет записано в строку. | ||
+ | Решение. | ||
+ | Считаем строку, разделим ее методом Split. Получившийся массив в цикле foreach будем парсить в integer. | ||
+ | <source lang="delphi"> | ||
+ | if not Integer.TryParse(str, number) then // Если формат файла не верный | ||
+ | raise new System.FOrmatException; // то сгенерируем исключение</source> | ||
+ | '''Исключение''' - это объект определенного класса. | ||
+ | Все исключения, которые можно выбрасывать, являются классами System.Exception. | ||
+ | |||
+ | '''Ужасная вещь, которую надо написать, а потом аккуратненько потолще зачеркнуть''' | ||
+ | |||
+ | Пример необоснованного использования исключений. | ||
+ | |||
+ | Для создания своего исключения достаточно сделать потомок класса Exception. | ||
+ | |||
+ | <h4>Исключения в Java</h4> | ||
+ | |||
+ | Иерархия исключений. Контролирумое исключение, неконтролирумое исключение. Объявление исключения. Блоки try - catch и try - finally. | ||
+ | |||
+ | <h4>Исключения в С++</h4> | ||
+ | |||
+ | Все не как у всех. | ||
+ | - Выбрасывать можно все что угодно (даже Зоопарк). | ||
+ | - Исключения генерируются по значению, а перехватываются по ссылке. | ||
+ | - finally нет. Это связано с тем, что когда идет развертка стека автоматически вызываются деструкторы. | ||
+ | |||
+ | Стандартные способы обработки исключений | ||
− | + | ==Ссылки== | |
+ | *[[Конспекты|Другие конспекты]] | ||
+ | [[Категория:Конспекты]] | ||
+ | [[Категория:Языки программирования]] |
Текущая версия на 18:04, 4 июня 2009
Содержание
- 1 Лекция 1
- 2 Лекция 2
- 3 Лекция 3 (16.09.08)
- 4 Лекция 4 (23.09.08)
- 5 Лекция 5 (30.09.08)
- 6 Лекция 6 (07.10.08)
- 7 Лекция 7 (14.10.08)
- 8 Лекция 8 (21.10.08)
- 9 Лекция 9 (28.10.08)
- 10 Лекция 10 (11.11.08)
- 11 Лекция 11 (18.11.08)
- 12 Лекция 12 (25.11.08)
- 13 Лекция 13 (02.12.08)
- 14 Лекция 14 (09.12.08)
- 15 Лекция 15 (16.12.08)
- 16 Лекция 16 (23.12.08)
- 17 Ссылки
Лекция 1
Основы Java. Понятие виртуальной машины, схема компиляции и выполнения. Преимущества и недостатки виртуальной машины.
Первая программа на Java.
Переменные и константы. Стандартные типы. Правила приведения числовых типов.
Ввод-вывод. Класс Scanner.
Операторы.
Перечислимый тип.
Класс String, некоторые его методы.
Массивы. Оператор for(x: a). Класс java.util.Arrays.
Двумерные массивы.
Функции. Параметры. Отсутствие ссылочных параметров.
Лекция 2
АТД. Интерфейс, реализация, принцип сокрытия реализации.
Реализация АТД в виде класса.
Класс как модуль и как тип данных.
Инкапсуляция как объединение методов и полей в одной "капсуле".
Защита доступа в классе (private, public).
Синтаксис классов в PABC.NET и в Java. Вызов конструкторов.
Определение методов внутри и вне интерфейса класса (PascalABC.NET). Достоинства и недостатки каждого способа.
Класс Стек и его реализация на основе массива (PascalABC.NET, Java). Клиентская программа для класса Стек (PascalABC.NET, Java).
Вывод: семантика классов в PascalABC.NET и Java практически идентична, различается только синтаксис.
Хранение объектов классов в памяти. Ссылочная объектная модель. Присваивание и сравнение объектов. Нулевое значение объектной переменной.
Передача параметров по ссылке в Java - необходимость создания класса-обертки.
Лекция 3 (16.09.08)
Размерная модель классов в C++. Хранение объектов классов в памяти. Присваивание объектов.
Определение методов внутри и вне интерфейса класса (C++).
Где следует размещать код интерфейса класса и код реализации его методов.
Методы в записях PascalABC.NET. Размерная модель данных для записей.
Объекты C++ в динамической памяти. Необходимость явного освобождения памяти.
Лекция 4 (23.09.08)
Статические методы и поля в PascalABC.NET, Java и C++.
Статические конструкторы в PascalABC.NET, Java.
Класс Динамический массив (PascalABC.NET).
Свойства. Свойства с индексами.
Стандартный класс двусвязного списка в библиотеке .NET.
Понятие итератора. Защита доступа. Интерфейс итератора. Итерация по списку с помощью итератора. Итерация с помощью foreach.
Класс Множество на PascalABC.NET. Реализация с помощью списков и с помощью бинарных деревьев поиска.
Идея ускорения доступа. Хеширование. Хеш-таблица, хеш-функция.
Лекция 5 (30.09.08)
Реализация множества на основе хеш-таблицы.
Класс Ассоциативный массив на PascalABC.NET на основе списка.
Классы ассоциативных массивов в стандартной библиотеке NET. Итератор ассоциативного массива. Цикл foreach по ассоциативному массиву.
Контейнерные классы в Java: обзор. Классы-обертки (Integer, Double).
Стандартный класс двусвязного списка в библиотеке Java. Итератор списка. Итерация по списку.
Пакеты в Java. Пример использования пакетов.
Лекция 6 (07.10.08)
Библиотеки в PascalABC.NET. Многоязыковость. Отличие библиотек от модулей.
Библиотеки JAR в Java.
Перегрузка операций в C++ - общие правила.
Класс Date. Перегрузка операций +, +=, ++ (префиксной и постфиксной), ==, !=.
Дружественные функции в C++.
Лекция 7 (14.10.08)
Перегрузка операций << и >>
С++: выделение динамической памяти в конструкторе (на примере класса myvector).
Перегрузка операции [].
Деструкторы. Момент вызова деструктора.
Динамический массив в динамической памяти. Момент вызова конструктора и деструктора.
Моделирование ссылочной модели данных средствами C++.
Конструктор копии. КК, генерируемый по умолчанию. Когда его не достаточно.
Операция присваивания. Операция присваивания, генерируемая по умолчанию.
Ситуации, в которых вызывается конструктор копии.
Лекция 8 (21.10.08)
Шаблон класса myvector<T>. Особенности компиляции шаблонов. Где надо размещать описания шаблонов и почему.
Описание функции-члена вне интерфейса класса.
Понятие инстанцирования шаблона класса. Два уровня ошибок компиляции шаблонов: при компиляции собственно шаблона и в момент инстанцирования шаблона.
Массив объектов класса и роль конструктора по умолчанию.
Операция (). Примеры: реализация класса matrix и объекты-функции. Преимущества объектов-функций.
Лекция 9 (28.10.08)
Класс frac. Конструктор преобразования и операция приведения типа.
Ключевое слово explicit и запрет неявного преобразования.
Перегрузка операций в PascalABC.NET (на примере класса Frac).
Агрегация класов и подобъекты (C++).
Вызов конструктора подобъекта.
Роль конструктора по умолчанию.
Порядок вызова конструкторов и деструкторов.
Клонирование и присваивание объектов в языках со ссылочной моделью хранения объектов (на примере PascalABC.NET)
Лекция 10 (11.11.08)
Наследование. Основные определения: базовый класс-производный класс, предок-потомок, надкласс-подкласс. Цели наследования. Описание на PABC.NET классов Student и SeniorSudent (наследник Student).
Наследование - это расширение или сужение? Наследование - это расширение интерфейса класса, но сужение количества представителей.
Переопределение и замещающие функции (PABC.NET). Ключевое слово inherited.
Вызов конструктора предка в конструкторе потомка (PABC.NET). Конструктор для SeniorSudent. Рекомендуется вызывать конструкторы предков первыми в конструкторе потомка, т.к. объект предка д.б. создан перед какими-то следующими действиями. В большинстве языков программирования конструкторы предка вызываются первыми принудительно. В качестве примера написали классы Queue<T> и CountingQueue<T> (наследник Queue с одним дополнительным полем - количеством элементов).
Принцип "Открыт-закрыт". "Код должен быть закрыт от изменения своего текста, но открыт для модификации своего поведения." Причины закрытия и открытия кода.
Наследование и включение. Понятие включения (физического и логического). Изображение отношений наследования и включения на UML-диаграммах.
Что выбрать — наследование или включение? Напишем классы Queue<T> и CountingQueue<T> с использованием включения (PABC.NET). Понятие делегирования. Достоинства наследования. Пример плохого использования наследования:
Circle = class( Point );
В обоих классах есть реализация конструктора и процедуры Draw.
Примеры с неоднозначным выбором наследования или включения.
Лекция 11 (18.11.08)
Наследование и выявление общего предка. Даны UML-нотации классов Student и Teacher. Заметим, что у них есть общие поля и методы. Решаем вынести их в базовый класс Person. Реализовали класс Teacher на Java, в т. ч. конструктор c super(...).
Вид доступа protected
Что значит protected. protected нарушает инкапсуляцию. Пример наследования Square от Rectangle на PABC.NET. Приходим к выводу, что иногда необходимо обращаться к полям и в этом случае следует использовать protected.
Класс Object — предок всех классов
Дан интерфейс класса Object в Java и .NET (не полный - из 4 и 3 методов соответственно).
Присваивание в иерархии Предок–потомок. Описаны переменные s (Student) и p (Person). Пытаемся одному присвоить другое. Делаем вывод, что переменная производного класса может быть неявно преобразована к типу базового класса, но не наоборот. Рассматриваем случай явного приведения типов. Вводятся понятия UpCast и DownCast.
Операции is и as (PABC.NET) и instanceof (Java)
Определения статического и динамического типа переменной.
Два варианта использования операций is и as
Вариант 1.
PascalABC.NET
if p is Student then // не точное совпадение типов, а то, является ли p разновидностью Student
Student(p).ChangeCourse(4);
Java
if (p instanceof Student)
((Student)p).changeCourse(4);
Вариант 2.
PascalABC.NET
var s: Student := p as Student;
Java
Student st = (Student) p;
Лекция 12 (25.11.08)
Наследование в С++
Вспомним наш излюбленный пример наследования на примере классов Person и Student. В предположении, что класс Person уже описан, опишем на С++ класс Student. Одно из полей Student типа char*. Понятие публичного наследования. Особенности вызова конструктора (деструктора) предка в конструкторе (деструкторе) потомка. Пример использования char* надуман! Не пытайтесь повторить это дома!
Совместимость по присваиванию и преобразование типов в иерархии Предок–Потомок
Описали переменные p (Person) и s (Student). Они хранятся на стеке (размерная модель). Пытаемся присвоить одному другое. Делаем вывод об отбрасывании дополнительных полей производного класса при присваивании.
Совместимость по присваиванию для типов указателей и ссылок
Присвоение указателям (ссылкам): UpCast - неявно, DownCast - только явно (с помощью операции static_cast).
Конструктор копии и оператор присваивания для класса Student.
Полиморфизм и виртуальные методы
(Java) Пусть в классах Person и Student есть метод print().
Рассмотрим код:
Person p = new Student(...);
p.print();
Метод print() какого класса будет вызван? В Java - класса Student, а в OP и C++ (по умолчанию) - класса Person.
Понятия раннего и позднего связывания.
Реализация позднего связывания на OP. Виртуальные методы. Модификатор virtual и ключевое слово override. Определение полиморфизма.
Реализация полиморфизма в С++. Аналогично в классах Person и Student описан метод print(). Присвоим объекту класса Person объект класса Student и вызовем для него метод print(). Вызовется print() класса Person. Сделаем метод виртуальным (virtual в заголовке). Все равно будет вызван метод класса Person. Причина: при присваивании происходит отбрасывание дополнительных полей и потеря информации о типе. Выход: полиморфизм в С++ работает только через указатели или ссылки на базовый класс.
В Java все методы (за исключением статических) виртуальные.
Лекция 13 (02.12.08)
Виртуальные методы как блоки для замены кода
1. При разработке базового класса надо думать о будущем коде.
2. Полиморфизм заставляет работать уже откомпилированный код совершенно по-другому. Определение полиморфного объекта (объекта, обладающего полиморфным поведением), полиморфной подпрогораммы.
Виртуальные методы в классе Object
В С++ нет класса, базового для всей иерархии классов. Рассмотрели пример переопределения метода ToString класса Object на PABC.NET.
Цепочки виртуальности и ее разрыв
Определение цепочки виртуальности.
Алгоритм поиска в цепочке виртуальности метода, который следует вызывать.
Разрыв цепочки виртуальности.
Идентификатор reintroduce (NET).
С++ - виртуальные деструкторы
Конструкторы в С++ виртуальными быть не могут, а деструкторы могут. Правило: Если в классе есть хотя бы одна виртуальная функция или нет, но в подклассах они могут появиться, то деструктор этого класс следует сделать виртуальным. Правило: Если деструктор базового класса виртуальный, то сгенерированный деструктор также будет виртуальным.
Полиморфные контейнеры
Полиморфный контейнер в .NET
Рассмотрели пример на PascalABC.NET в котором Shape
- это предок классов геометрических фигур. Хотим написать методы Draw и Hide. Вместо этого (вместе с этим) дали определение абстрактного метода и абстрактного класса. Идем дальше. Реализовали класс Shape. В глаза бросается метод MoveTo, который вызывает два абстрактных виртуальных метода (Hide и Draw). Интересно, он вообще работает... Ответ: да, работает, ведь перед Hide и Draw неявно стоит self - ссылка, - как раз то, что надо для виртуальных методов.
Заполнили List<Shape> объектами производного от Shape класса, в одном цикле foreach нарисовали их, в другом переместили.
Определение полиморфного контейнера.
Если класс абстрактный, то объекты этого класса создавать нельзя.
Полиморфный контейнер на С++
Написали тот же класс Shape на С++. Здесь Hide и Draw называются уже чисто виртуальными ( =0 ). Затем заполнили vector<Shape*> объектами производного класса, в цикле (с итератором) нарисовали их, а потом еще и переметили на заданный ветктор. Полиморфное клонирование в полиморфном контейнере.
Система RTTI в С++
Если в классе не определено ни одной виртуальной функции, то аналогии is и as ввести нельзя. Аналогии операций is и as.
1. Операция dynamic_cast
dynamic_cast - это полный аналог as.
dynamic_cast<Student*>(pperson) == 0 - аналог is.
Лекция 14 (09.12.08)
2. Операция typeid и структура type_info
Посмотрим на поля и методы type_info. Попробуем повыводить на консоль typeid(выражение) и typeid(тип).
Задача (С++) о раскраске всех треугольников из vector<Shape*> - потомков класса Shape:
1-ый способ: Покрасить все треугольники и их наследников.
2-ой способ: Покрасить все треугольники, но не производные классы.
3-ий способ: Покрасить все треугольники, но не производные классы, используя только полиморфизм.
Таблица виртуальных методов - внутренний механизм реализации полиморфизма
Рассмотрели иерархию классов (A ◅— B ◅— C) с не виртуальными и виртуальными методами и с разрывом цепочки виртуальности. Объявили переменную p:
var p: A;
инициализировали:
p := new A;
Затем последовательно присвоили p ссылки на B и на C и смотрели что происходит в памяти после каждого оператора присваивания. Введены понятия VMT и vptr. Накладные расходы на вызов виртуальных методов (по памяти и по времени).
Интерфейсы (.NET и Java)
Определение интерфейса. Описание основных интерфейсов. Пример объявления класса, поддерживающего некоторые интерфейсы.
Лекция 15 (16.12.08)
Пример реализации интерфейса в Java (класс Student реализует ICloneable).
Интерфейсы, совместимость по присваиванию, преобразование типов, цепочки виртуальности
(все последующие примеры в .NET)
Пример реализации интерфейса (класс Student реализует ICloneable). Описание переменной типа интерфейс. Правила присваивания ей объектов различных классов. Полиморфный контейнер объектов, поддерживающих интерфейс ICloneable. Цепочка виртуальности, задаваемая интерфейсом.
UpCast, DownCast и CrossCast для интерфейсов
Написали два интерфейса и реализующий их класс.
var im1: IMy1;
im2: IMy2;
m : MyClass;
begin
im1 := m; // UpCast - неявно
m := MyClass(im2); // DownCast - только явно
im2 := IMy2(im1); // CrossCast - только явно
end;
Наследование интерфейса и наследование реализации
Понятия наследования интерфейса и наследования реализации. В каких случаях предпочтительнее наследование интерфейса, а в каких наследование реализации? Пример наследования от многих классов (как бы он мог выглядеть в .NET), проблемы такого наследования и решение проблем с помощью интерфейсов.
Принцип подстановки
Во всех ситуациях, в которых может использоваться объект базового класса, можно подставить объект производного класса без потери функциональности.
Принцип подстановки выполняется не всегда. Например, при наследовании с ограничением (круг от эллипса или вектор от матрицы) принцип подстановки не выполняется.
Принцип подстановки всегда выполняется если используется полиморфизм, а также при наследовании интерфейса.
Стандартные интерфейсы .NET
Пример 1. Интерфейс IComparable и его использование.
Пример 2. Интерфейс IComparer и его использование.
Лекция 16 (23.12.08)
Исключения
Исключения в PABC.NET
Задача 1. Есть набор файлов текстового формата. Эти файлы содержат целые числа, разделенные пробелами и символами перехода на новую строку. Пользователь вводит имя файла. Программа выдает сумму чисел в файле.
Решение. Начнем с самой вложенной функции.
function EvalSum( data: Text ): integer;
begin
Result := 0;
while not SeekOEof(data) do
begin
var a: integer;
Read( data, a ); // Проблема 2
Result += a;
end;
end;
Функция обработки файла
function ProcessFile( fName: string ):integer;
begin
var f: text;
Assign( f, fName );
Reset(f); // Проблема 1
Result := EvalSum(f);
Close(f);
end;
Далее напишем бесконечный цикл, в котором будем спрашивать у пользователя имя файла и обрабатывать его. Итого: имеем две проблемы:
проблема 1: Файл может не существовать. проблема 2: В файле могут быть неправильные данные.
Решение 1 проблемы 1. Можно поставить перед Reset(f) условный оператор
Если файл существует открой его на чтение
Сейчас, когда мы работаем в многозадачной среде может быть такое, что кто-то удалил файл после выполнения if'а, но перед Reset. Это плохое решение.
Решение 1 проблемы 2. Можно считывать не integer, а string и пытаться преобразовать к интам, Но проблема вот в чем: если мы не смогли преобразовать, придется написать
Writeln('Ошибка');
А что если EvalSum используется в оконном приложении? Куда выведется результат? В воздух?
Решение 2 проблемы 2. Пусть функция возвращает код ошибки. Для этого были придуманы исключения.
В коде с комментарием Проблема PABC.NET генерирует исключение.
while true do
begin
var fName : string;
Realdn( fName );
try
var sum := ProcessFile(fName);
writeln(sum);
except
Writeln('Ошибка'); // Хотелось бы поконкретней
end;
end;
Понятия try-блока и секции обработки исключений.
try
var sum := ProcessFile(fName);
writeln(sum);
except
on System.IO.FileNotFoundException do
Writeln('Файл отсутствует');
on System.FormatException do
Writeln('Неверный формат файла');
end;
То,_что_надо_отвечать_на_экзамене_на_вопросы,_касательные_исключений
Генерация исключения прерывает нормальное выполнение подрограммы и приводит к раскрутке стека вызовов вплоть до обнаружения соответствующего try-блока.
try
...
except
on ... do ...
on ... do ...
...
else
...
end;
В таком try-блоке будут обработаны все исключения без исключения(c).
Закрыть файл мы можем только там, где открыли.
function ProcessFile( fName: string ):integer;
begin
var f: text;
Assign( f, fName );
Reset(f);
try
Result := EvalSum(f);
finally
Close(f); // действия в блоке finally будут выполнены не зависимо от исключения
end;
end;
Задача 2. Модифицируем задачу 1. Предположим все целые числа разделены двоеточием. В этом случае все содержимое файла будет записано в строку. Решение. Считаем строку, разделим ее методом Split. Получившийся массив в цикле foreach будем парсить в integer.
if not Integer.TryParse(str, number) then // Если формат файла не верный
raise new System.FOrmatException; // то сгенерируем исключение
Исключение - это объект определенного класса. Все исключения, которые можно выбрасывать, являются классами System.Exception.
Ужасная вещь, которую надо написать, а потом аккуратненько потолще зачеркнуть
Пример необоснованного использования исключений.
Для создания своего исключения достаточно сделать потомок класса Exception.
Исключения в Java
Иерархия исключений. Контролирумое исключение, неконтролирумое исключение. Объявление исключения. Блоки try - catch и try - finally.
Исключения в С++
Все не как у всех. - Выбрасывать можно все что угодно (даже Зоопарк). - Исключения генерируются по значению, а перехватываются по ссылке. - finally нет. Это связано с тем, что когда идет развертка стека автоматически вызываются деструкторы.
Стандартные способы обработки исключений