Языки программирования, 24 лекция (от 30 ноября)
Материал из eSyr's wiki.
Предыдущая лекция | Следующая лекция
Содержание |
[править] Глава1. Наследование
[править] П. 3 .Замечание
- Запрещение наследования. Джава – наследование класса или метода запрещено, модификатор final. final перед именем поля означает, что присваивание этому полю в конструкторе последнее, то есть константа. Далее мы знаем finally, finalize. Если final public class Х, то из этого класса наследовать ничего нельзя, так что любая попытка ввести класс class Y extends X. Перед методом финал значи, что его нельзя переопределять. Если будет другой профиль, то это вещь допустимая. Шарп – вместо final – sealed – закрытые (для наследования) класы. Многие классы создаются для одной конкретной цели, и являются компонентами. Программы-дизайнеры называют конструктами по аналогии с джетскими конструкторами. Пример: класс форма, любая форма является наследником формы. Есть классы, от которых только наследовать, использовать нельзя – абстрактные классы
- Доступ (видимость). В шарп и джава новый вид доступа – пакетный/внутренний. Пакетный доступ, если он есть, то мы доверяем своим коллегам больше. С этой точки зрения обсудим понятие защищённосит в языке джава. С С++ всё понятно, так как там пространтвто имён введено в 90-е годы и только для большых проектов, над которым работает множество программистов. Оно свойств модульности не имеет. С этой точки зрения семантик protercted очень проста – мы можем делать с ним, что хотим. В шар есть обычный протектед, а есть интёрнал протектед, то есть мы можем обращаться из произвольнеых классов в пределах пакета. Это дополнительные уровни доступа, и они расширяют модель С++. Есть такой термин, который стал популярен в последнее время, контракт. Мы определяем, какие члены прайвэт, протектед. Класс говорит, что давайте заключим контракт. Если вы используете мои интерфейсы, то вы подписались по дконтрактом. Контракт – совокупность мпетодов, которые мы открываем для доступа. Есть ещё один. В С++ этого не хватало. Нужны были классы или фугкции, которые имеют полные права доступа. Друг – попытка решить эту проблему .В джаве это решается тем, что введён пакетный доступ. Пакетный доступ сильнее, чем защищённый – любой защищённый член обладает след семантикой: имеется доступ через this. Если член объявлен как защищённый, то в рамках данного пакета имеют доступ функции, которые объявлены через ссылкуна этот класс или его потомки.
Есть protected X -> protected pro
Я могу где-то выводить ихэ того класса, через ссылку this я имею полный доступ к этому самому pro. Представим себе, что внутри класса Y объявляется другой член класса Y, Y {Y b; b.pro} писать можно. Если вместо Y Х, то быдет ошибка, если не в пакете, если в пактее, то ему доверия больше. Если Вы объявлены в другом пакете, то можно работать или через ссылку на сам класс, иили от его потомков. Если наследовали, топодписываете новый контракт, который поход на старый, но может быть изменён.Х создаётся без учёта того, что в классе Y. В джаве тоже некая иерархия возникает: pprivate – пакетный доступ – protected – public.
С точки зрения языков, которые ОО, но модульные
[править] Оберон: есть модуль, и имена, которы помечены звёздочкой
MODULE M TYPE T* = RECORD I : INTEGER; END;
Виден тип, но не виден И. Кто видет И? Функции, которые описаны в модуле
TYPE T1* = RECORD(T) /// END;
Если считат модуль пакетам, то ко всем объектам и переменным, то вот та доступ только пакетный, причём все объявленные классы имеют доступ.
IMPORT M; TYPE T1* = RECORD(M.T)
Функция имеет доступ? По определению нет.
Расширять класс можно только в пределах модуля. Защёщиённых чденов нет, ибо либо они обладают пакетным доступом, либо, если наследуем из другого языка, одв
[править] Ада:
Встала эта же проблема, но более остро. При нааследовании
package P is Type T istagged record private type T = tagged record end record end p
Если м ы хотим использовать пакет Т, то мы должны к нему присоеднисться.
package P .DP is
Имя точечное. Эта проблема вставала, когда обсуждали раздельную компилчции.
Точно так же кАк в джаве и шарп цепляемчя к пакету, тут тоже цепляемся и получаем доступ к приватной части пакета. И можем писать:
type T1 is new T with tagged record /// end record
«то цена за то, что мы расширяем уже существующий язык. В Аде 83 ОО уже было, но их рассматривали как некую академическую вещь.
Вложенные области видимочсти: если name является именем функции, то есть варианты, если нет – сокрытие. Такая ситуация допустима за счёт того, что области вложенные
В джава есть ссылка на базовый класс (супер) Шарп – base, Дельфи – inherited Create.
Если мы допускаем, если нейм является именем функции, то тут начинается... В JD#++ допустима перегрузка функций. Можно переопределить функцию f с таким же именем, но другим профилем: это называется перегрузка. Ещё возможна ситуация, когда переопределяется функция с тем же самым профилем – происходит замещение, переопределение. Суть – когда определяют в производном классе функцию с теми же параметрами. По правилам языка она либо замещает функцию, либо, либо замещает все, как в С++:
class Base { public: void f(int); };
class Denied { public: void f(const char * str) }
Derived d; d.f(«Hello!»);
к счастью, здесь всё будет нормально
d.f(5)
очевидно, о какой функции иёт речь, но обратиться к ней нельзя. Ошибка: «нет преобр от инт к чар»
Чтобы было видно, нужно в Derived
void f(int i) {Base.f(i);}
В шарп можно по-другому сделать.
[править] Шарп:
не существует неявного преобразования из инт в дабл.
[править] Глава 2. Динамич связывание методов (полиморф)
[править] Оберон: Расширение типов
t принадлежит Т1 – t принадлежит T t принаджлежит Т – t-ссылка -указатель
procedure Draw(var P: Figure)
P is Line t is T1 – динамич идентиф типов
T1(t) – приведение типа
P(Line).
Написали огромный иф:
if p is line then drawline(p(ilne)); ELSIF P is rect then drawrect(p(rect)) ELSIF ...
Подобного рода подход решает проблему, связанную с расширением типов, когда обсуждали записи с вариантами.
Но при добавление нового класса надо все эти ифы исправлять.
Но истинное ООП становится тогда когда появляется динамич связывание методов. В обероне это могло быть реализовано с помощью типов процедур. Есть тип обработчик:
Теперть метод draw выглядит по-другому:
P.DrawProc(P);)
Чтобы добавить новый класс, нужно вывести
TYPE PatRect. = RECORD(RECT) end;
Init(var p:patrect; DrawProc:Dproc); begin p.drawproc:=drawproc; end;
Третье, что нужно делать: написать процедуру отрисовки
procedure DrawPatRect(VAR P:FIGURE); begin if P is PatRect then begin P(Patrect).... end; end;
В обероне есть спецоператор, упрощающий написание подобного рода вещеё.
Бльше ничего писать не надо.
Недостатки: сам синтаксис избыточен. Кроме того, кто нас завтавит выхзвать процедуру? никто. К Оберону-2 была добавлена динамическая привязка меодов. То же самое, но на уровне языка. Мы пишем:
Если есть тип Т
T1 = RECORD(T)
мы можем сказать, что есть спецпроцедура, и это единст существ расшир О-2 по сравнениею сО
Процедура, динамически привязанная
PROCEDURE (VAR F:Figure) Draw(); - в качестве особого параметра передаётся фигура. В робероне запрещается перекрытие имёне, кроме этого случая. Аналог в С++ и джаве – this.
Теперь, ||синтаксис Оберона и Модулы-2 требует больших букв в идентификаторах и ваще везде
TYPE Line = RECORD(Figure)(Figure) ... PROCEDURE (VAR L:Line) Draw();
параметром должно быть нечто, обладающее дин типом – указатели или ссылки.
Есть список из фигур, и тогда DrawAll будет выглядеть очень просто:
DrawAll(L:ListFig) WHILE L # NIL DO L.Fig.Draw();
ListFigLink = RECORD Fig : Pfigure; NEXT:ListFigLing END;
ListFig = POINTER TO ListFigLink;
Pfigure = POINTER TO Figure;
Что бросается в глаза: L является указателем, обратим внимание, что никакого стрелочка, явного оператора разыменования. В ряде совр языков нет разницы между объектами и указателями на них. Fig.Draw – обращение к процедуре дин привяз типа. Какой именно метод Draw будет вызываться? Динамически определится. Если тип Line, то будет вызван DrawLine. Если процедура не описана, берётся у базового класса. Это полный аналог вирт методов в С++.
Какн арисовать прямоуг: нарисовать границу и внутри паттерн.
В Оберон 2 введена специальная конструкция: Draw^ - ,вызвать Draw для базового класса.
Язык О-2 – полный и безоговорочный ОО-язык.
[править] C++
class X { public: virtual void f(); };
class Y: public X {
void f(); - virtual писать не надо, и возвр значения должны совпадать. В последней версии стандарта можно возвращать указатель или ссылку на произвольный класс. }
Эту цепочку наследований можно...
Z: void F(); ...
Вирт функция. Виртуальность – способ вызова. Если мы описываем функцию как виртуальную, о она будет как виртуальная. Он может быть через объект с дин типом. X a; a.f; - здесь никакого дин вызова нет, вызывается X::f. Вирт методы, если они вызываются через указатели или ссылку, то он вызывается в зависимости от дин типа указателя.
X * px; ... px->f();
Если метод не виртуальны, куда мы должны смотреть? На описание Х. Если метод невиртуальный, то смотрим на Х, и больше никуда не смотрим. Пусть везде ещё есть G() и она невиртуальная. Если px->g(); то совершенно неважно, что здесь происходит, всегда буит вызываться X::g(); Более того, оно может быть натолько статическим, что компилятор может заинлайнить её. Если компилятор не вдиит текста, то он говорит call G,
Всё зависит от динамического типа. Если вызывается явно через указатель-ссылку, то смотрим:
W * pw; pw->foo();
Смотри на описание класса W, если там виртуальная, то ничего сказать не можем. Если же foo невирт, то смотрим на него и статический вызов делаем.
Аналоги с другими ЯП: вирт функции
В С++ было такое ограничение, что если функция была невирт, то она не может стать виртуальной.
class Figure { protected: int X, Y;
public: virtual void Draw(); };
тогда процедура DrawAll будет переопределять соотв метод Draw и будет выглядить след образом:
void DrawAll(List l) { while(l) { l->Fig->Draw(); l=l->next; }
Что нужно сделать при добавлении очередного класса: вывести новый класс и перепопределить метод Draw и некрые другие. Перекомпилировать файд, перекомпоновать. Локальные изменения распространяются на весь код. Главное, чтобы они удовлетворяли контракту. В других ЯП дин связывание...
Draw^();
В С++ ситуация более гибкая:
py->f();
Мы не знаем, какая F() будет вызываитьсЯ, но можем указать это явно:
py->X::f();
Y: g() {f();} - непонятно, какая f вызывается, ибо это анало this->. Зис может быть не только игреком но и любым производным классом.
Z: наследует Y::g
pz->g(); - this будет Z
Вирт вызов снимается
T::f();
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
Календарь
чт | вт | чт | вт | чт | вт | чт | вт | чт | вт | |
Сентябрь
| 05 | 07 | 12 | 14 | 19 | 21 | 26 | 28 | ||
Октябрь
| 03 | 05 | 10 | 12 | 17 | 19 | 24 | 26 | 31 | |
Ноябрь
| 02 | 14 | 16 | 21 | 23 | 28 | 30 | |||
Декабрь
| 05 | 07 | 12 | 14 |
Материалы к экзамену
Сравнение языков программирования