Языки программирования, 26 лекция (от 07 декабря)
Материал из eSyr's wiki.
Предыдущая лекция | Следующая лекция
Содержание |
Гл. 3. АК и интерфейсы
АК служит интерфесовм для производных классов.
АК <- клиенты исп только ссылки на абстрактные классы, конкретные реализации методов в произв классах.
АК нет только в Обероне-2.
Вопрос не в том, есть АК или нет, вопрос в том, поддерживается ли это на аппаратном уровне.
В ЯП, где отсутствуют стандартные языковые ср-ва для АК, используются другие средства, например Abstract в теле «вирт» функции.
С++
ЧВФ: virtual void f() = 0;
Нельзя создавать объекты абстрактных классов.
Наиболее ослабленная поддержка в Дельфи procedure P; abstract; - только дин связывание – по опред виртуальная.
В Дельфи можно заводить объекты абстр классов.
Можно сделать
t:=X.Create; t.P;
можно X1.Create и тогда t.P можно.
шарп, Джава
Абстракт клсаа – класс, в котором ест хотя бы один абстр метод АК – класс, перед ктоторым стоит abstract.
abstract class X { public abstract void f(); - единственный случай, конда ставится точка с запятой }
Ада 95
type T is abstract with null record;
T – это такой тип данных. Мы до этого говорили так. может быть произвольный ТТ
type TT is tagged record;
это пример типа, который может служить родитеоем какой-либо иерархии. довольно частая ситуация, когда null record. Что значит, что в типе нет данныХ? что они появятся. Если нет данных, то мы не можем никакие методы написать, поэтому можем написать только интерфесы. поэтому type TT1 tagged null record; редка, а abstract часто.
type AT is abstract with record ... end record; ... procedure P(X:AT) is abstract; - процедура обязана быть переопределена в одном из классов наследников.
Заводить методы можно только у абстрактного класса.
Остановимся на случае, когда не заводим данные.
Пример класса-фигуры – пример абстрактного класса, который может обладать как абстрактными методами, так и не абстрактными. Например, простейшая реализация метода Мув:
void Move (int dx; int dy)
заметим, что в простейшем случае с точки зрения ГИП, что нужно сделать: сменить точку привязки, после этого берём и перерисовываем.
{ x+=dx; y+=dy; Redraw(): }
В простейшем случае но прекрасно работает для любой фигуры.
АК может содержать данные, абстрактные методы...
В совр ЯП не включают множество, так как это слишком многогранное понятие. Его реализуют в виде дерева, хэш-таблицы... Поэтому большинство совр языков есть стандартные библиотеки.
Пользователю не нужно ничего знать о структуре множества, только интерфейс.
class Set { public: void Incl(X & x); void Excl(X & x); Set Union(Set & s); ... }
Можно сделать из АК типичный АТД. Но тогда будет только одна реализация.
Но в больших проектах бывает, что в одних местах хороша одна реализация, в других – другая.
У класса верхнего уровня никаких данных не будет, поэтому методы чисто виртуальные.
Интерфейс как языковое понятие – класс, в котором все члены – чисто виртуальные функции.
У него могут быть статические, протектед или паблик члекны, и все нестатические должны быть паблик.
Почему в Джаве есть понятие интерфейса, в а в С++. Потому, что в С++ есть множественно наследование, а в джаве-шарп нет.
Множественное наслед порождает проблемы – проблема конфликта имён. Самая главная проблема – проблема реализации вирт методов. Когда вызываем метод класса Y, то надо передавать указатель на Y. Оказывается, что указатель this надо модифицировать. При множ наследовании надо делать много таблиц вирт методов.
Вевли отдельное понятие интерфейса, потому что их можно безопасно и надёжно множественно наследовать.
Сделали интерфейс Set. Можем сделать класс
class SlistSet: public Set, public Slist { public: void Incl(...) {...} void Excl(...) ... }
осталось завести статическую функцию Make, которая возвращает Set, но генерит нужный класс.
Где может пригодиться приватное наследование – чтобы скрыть от пользователя даже возможность реализации ...
Интерфес – в чистом виде контракт без реализации.
В шарп, джава возникает понятие интерфейса:
interface Int { прототипы объявления статических методов }
class X extends Y implements I1, I2, ... {...}
если класс наслед интерфейсЮ но не реализует нек-рые методы, то он становится абстрактным.
шарп:
class X: Y, I1, I2, ... { ... } - только одна база может быть классом
Если класс не реализует хотя бы один методж, то он сразу становится абстрактным, и это надо подчеркнуть спецификатором abstract.
Что делать – инт или абстрактный класс?
Если чувствуется, что появляются поля или неабстрактные методы, то АК, иначе интерфейс.
Интерфеймс – в голом виде один контракт.
Класс говорит – я поддерживаю контракт, и контрактов может быть много.
Icomparable – меня можно сравнивать
int compareTo
Iserializable – меня можно сохранять
Суть технологии oLE – они поддерживают OLE, если они поддерживают набор интерфейсов. Все соотв инт-сы стандартизованы. Например, есть инт-с IdataObject – позволяет передавать данные от одного процесса к другому. Понятие Clipboard основано на понячтии IDataObject.
Совр ЯП идуть дальше. Например, если известно, что класс поддерживает интерфейс, то с ним ЯП может общаться специальным образом.
Компилятор генерирует код, с зависимости от того, какие интерфейсы поддерживает класс. В шарп то же самое – foreach – IEnumerable, IEnumerator. IDisposable. C# - System, Java – java.lang
Интерфейсы-маркеры. Они вообще не содержат членов, но компилятор знает о них кое-что ещё. Например, Clonable. Это пустой интерфейс. Serializable.
В шарп интерфейсов-маркеров нет, но там есть атрибут. Атрибут это нечто. Перед перечислением могут стоять тарибуты-флаги, которые что-то говорят. Например, то, что можно применять побитовые опреации.
enum OpenMode { Read = 1; Write = 2; ReadWrite = Read.Write; }
Главная проблема множ наследования – конфлик имён.
Пример: Draw. Если мы реализуем колобу карт, то мы её можно и рисовать, и раздавать, например, в Idrawable и IcarDealer. В этом случае ничег оумного, кроме как выбрать одну из реализаций.
I1 – Run() I2 – Run() class X: I1, I2 public void Run() {...} void Run() {...} - допустима? да. Мы приватным образом переопределили метод. Y y = new Y() Y.Run(); - нельзя, если второе объявление I1 i = (I10 y; i.Run();
Как выглядит интерфейс: Ссылка на ТВМ Ссылка this
В случае реальной реализ интерфейсов, мы указываем здесь, что у нас здесь возникает конфлик по методу Run(), то если мы напишем oublc void Run() {..}, то непонятно, к ккакому это интерфейсу. Поэтому мы должны явным образом void I1.Run() {...} void I2.Run() {...} В случае реальной реализ интерфейсов тут не указывается модификатор, то он прайвэт. Их приватность заключается в том, что мы не имеем права выызвать Run, мы должны явно привести к интерфейсу. ((I1)X).Run(). Когда оба интерфейса сделаны явно, то можно сделать public void Run() {((I1)this).Run()}
class Y: X, I1 {} - нормально ((I1)y).f() - если не переопр в y, то из х, иначе из у.
Глава 4. Дополнительные возможности
п.1 Дин инф-ция о типе
RTTI.
Поскольу в любом ОО-языке появляется динамисеский тип, то вполне резонно задать вопрос, можно ли узнать его во время выполнения.
С++
Стауструп указывал, что в первыйх версиях С++ вообще небыло никакой Диамической идентиф типов. К сожалению, вопрос дин идент типа – если мы можем узнавать тип во время вып, то это то же самое, что делали программисты ранее, моделируя это объединением и
switch (p->type) { - ручная жиспетчеризация case T1: ... case T2: ... }
Вместо всего вот этого должно быть p->virtMethod() - дин диспетчиризация
С. не хотел вводить RRTI, чтобы не провоцировать программистов. Заметим, что совр программисты программируют так же, что ужасно.
шарп, дельфи:
Object o, берём из коллекции, и приводим его к нашему.
Но потом С. ввёл, так как каждый разработчки вводидл свои возможности RTTI.
В 90-е годы в с++ появилась RTTI, но прежде мы рассмотрим её во всех языках.
Самая простая – в языке Оберон.
RTTI полезна в случае исп. стандартных компонент. Например, есть Cdialog, все наши классы выводятся из Cdialog, Нам передаётся Cdialog, а мы его преобразовываем. Перобразование контроллируемое, а контроль осущ средствами RTTI.
Дин тип м- относится либо к ссылке, либо к указателю.
У ссылки есть стат тип, и дин тип. Дин тип либо совпадает, либо является наследником.
t is T1;
Для чего нужна проверка – чтобы делать безопасное преобразование.
Преобразование типа – страж типа
t(T1)
групповой страж типа:
WITH t(T1) DO ... - t трактуется как объект типа T1 END;
Типичное программирование на Оберон:
IF t is T1 THEN WITH t(T1) DO ... END ELSIF t is T2 THEN WITH ... END; END;
Java:
аналдоги первых двух конструкций есть
дин проверка типа – t instanceof T1 синтаксис: expr instanceof T – статич тип должен быть меньше или равен T страж типа – T1(t)
Дельфи:
expr as T; - если принадлежит, то происходит преобразование, если нет, то nil with t as T do – дин проверка типа, и если проходит, то преобр тип и все поля видимы непосредственно begin end;
шарп:
(T)e – всегда контролируемое
e as T
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 |
Материалы к экзамену
Сравнение языков программирования