лекции (2008) (by Михайлишин Алексей_ Жбанков Денис_ Щербинин Виктор_ Чеботарев Павел) (1160831), страница 12
Текст из файла (страница 12)
модуль.Дельфи:uses ...; - стазу делает все имена из перечисленных модулей непосредственновидимыми.М2:import ... – для имен, импортированных таким образом, нужно всегда писать имямодуля.from ЕК import … – непосредственный импорт (имена делаются непосредственновидимыми, не нужно писать имя модуля)В Обероне второй конструкции нет.При таком механизме связывания серверный модуль ничего не знает о клиентском. Кольцевые связи втаком случае – ошибка. Пример в Дельфи:unit m1;interfaceuses m2;unit m2;interfaceuses m1;Ошибка времени трансляции.Однако, следующее описание юнита m2 будет уже корректно, т.к. нужно оттранслировать интерфейс m2,затем интерфейс m1, затем implementation m2 и m1 (d в произвольном порядке):unit m2;interface…implementationuses m1;Стандартные библиотеки многих языков используют одностороннее связывание.
Недостаток – большиесписки импорта.Двусторонняя связь.Она реализована только в ЯП Ада (односторонняя связь в ней тоже реализована).При трансляции программы на ЯП Ада программист имеет две опции: либо собрать весь проект в один файли подать этот файл на трансляцию, либо разбить проект на отдельные ЕК и подавать на трансляцию покускам, а затем собрать эти куски воедино.
В ЯП Ада есть понятия первичных ЕК и вторичных ЕК.Первичные ЕК – те, которые нужны для трансляции других модулей. Пример: спецификация пакета илипроцедура. Они формируют трансляционную библиотеку.Вторичные ЕК – нужные сами по себе (тело пакета). Они формируют программную библиотеку.Односторонняя связь, указание контекста:WITH список_первичных_ЕК;USE список_2; - может отсутствоватьтекст ЕКсписок 2 – собственное подмножество список_первичных_ЕК.
Имена из пакетов из списка 2 становятсявидимыми непосредственно (для имен из список_первичных_ЕК\список_2 нужно указывать имя модуля).WITH – аналог import в М2.WITH Stacks; - подключает пакет Staks к нашему пространству имен.USE Stacks; - имена из Staks становятся видимыми непосредственно.Двусторонняя связь.Отличие Ады от других рассматриваемых ЯП – логические модули в этом ЯП могут быть вложеными.ЕК1:package Outer is ...;package Inner is ...;procedure P(x:t1, y:t2);...end Outer;Вложенность тел должна соответствовать вложенности спецификаций.ЕК2:package body Outer is......package body Inner is separate; -separate означает, что определение в другомместеprocedure P(x:t1, y:t2) is separate; - определение в другом местеend Outer;ЕК3: (неправильный вариант)WITH Outer;USE Outer;package body Inner isend Inner;Этот вариант неправильный, т.к.
чтобы откомпилировать ЕК3, нужно загрузить имена из implementationчасти Outer, а в этом варианте загружаются только имена из определения Outer. Здесь нужна двусторонняясвязь. Двусторонней связью может обладать только вторичная вложенная ЕК.Правильно ЕК3 нужно писать так:separate(Outer.Inner)package body Inner is ...end Inner;ЕК4:separate (Inner)procedure P(...)...end P;При изменении вторичных модулей надо перетранслировать только эти вторичные модули. При изменениипервичных модулей надо перетранслировать не только эти модули, но и все модули, зависящие от них.Цель механизма раздельной трансляции – минимизировать время перекомпиляции. При независимойтрансляции сам программист при сборке должен указывать зависимости.
При зависимой трансляциизависимости отслеживает компилятор, определяя их по тексту программы.III) Управление пространствами именПроблемы:1. Указание контекста трансляции. Как управлять контекстом трансляции?2. Дистрибуция (распространение). Что является единицей дистрибуции?(В С/С++ ЕК является файл)Проще всего в Java – там есть понятие пакета. Там ЕК является тоже файл, но он должен содержатьpackage имя_пакета;Любой класс, который может использоваться вне пакета, должен быть помечет public.Пример: любой пакет, транслирующийся в исполняемую программу, должен иметь публичный класс состатической функцией main определенной сигнатуры.package mine;public class Entry{public static int main(String Args[]){}}Пакет собирает ЕК воедино.
Он служит для указания контекста трансляции и как единица дистрибуции.Имена пакетов иерархические.package Proj1.Root.com.Foo;Изначально идея была в том, чтобы имя пакета отражало его положение на некотором сервере.import имя_пакета;import имя_пакета.*; - импорт всех имен из пакета в наше пространство именимя_пакета.имя класса – для того, чтобы так писать, не нужно предложения import.Proj1.Proj11.Proj111Proj1.Proj12Это не имеет отношения к зависимости пакетов друг от друга, а служит только для указания места виерархической файловой системе. В С++ и C# есть понятие пространства имен, которое ужеподразумевает именно зависимость вложенных пространств имен от внешних.[lect29]Пространство имен C#, C++Единица компиляции – файлЕдиница контекста – пространство именЕдиница дистрибьюции – сборкаЧасть 3.
ООП – языкиГлава 1. НаследованиеBase => Derived (базовый и производный классы), можно также использовать терминологию Smalltalk:Superclass => Subclass.C#, Java, Delphi – Object (TObject) – есть некий базовый класс для всех.C++, Оберон, Ада95 – нет общего класса.С++:Class Derived: public Base { //множественное наследование...};C#:Class Derived: Base ,Base1 … { //только один из base может быть классом...}Smalltalk:Class Subclass extends Superclass implements I1, I2 … {}//здесь тоже только один суперкласс и множество интерфейсовDelphi:type Derived = class(Base) begin ...
end;Оберон:TYPE BASE = RECORD ... END;TYPE DERIVED = RECORD(BASE) ... END;Ада – тегированные ТД:type Base is tagged record ... end;type Derived is new Base with record ... end; //or with null recordВсе языки позволяют линейное распределение памяти – дань эффективности реализации. В языке Smalltalkситуация другая. Subclass отдельно от Superclass. В Subclass есть некое поле, содержащее ссылку на объектсуперкласса. При этом поиск переменных или методов производится рекурсивно динамически. Естьвозможность динамически добавлять переменные или методы.
Проигрываем скорость, получаем гибкость.Поэтому Smalltalk неэффективен как язык индустриального программирования.Все функции существуют в одном экземпляре, с этой точки зрения накладных расходов(расширения памяти)не происходит.При наследовании каждый класс – это своя область действия.Class X{Int f;};Class Y: public X{Новые члены – своя область действияVoid f();};Y y;y.f – Вопрос: что это!? Ответ: Из класса Y!(скрытие)y.f() - недопустимоОбласть действия Y вложена в область действия X.
Т.о. допустимы совпадающие имена. С точки зрениястарых областей действия существуют понятия скрытие(новое имя) и перегрузка(только имена функций ипроцедур исключение - Ада – литералы перечисления). Т.е. есть разница между именами функций иименами остальных объектов.Class X{void f(int); - хоть виртуал хоть не виртуал – все равно скрытие.};Class Y: public X{Новые члены – своя область действияVoid f();};y.f(); - скрытие или перегрузка? Скрытие! Т.е. y.f(1); допустимо, а y.f(); нетчтобы обратиться к ф-ии из X y.X::f(1); или this->X::f(1)Разные области действия – перегрузки не может быть.
Есть еще переопределение – только для функций,которые обладают динамическим связыванием - если ф-я объявлена в базовом классе как виртуальная иимеет тот же прототипМодульные ЯП.Ада95, Оберон:MODULE M;TYPE BASE* = RECORDI: INREGER;J: REALEND;ENDM.Допустим, мы хотим сделать так:MODULE M1;IMPORT M;TYPE DERIVED* =RECORD(BASE)K: INTEGER;END;PROCEDURE P(VAR X: DERIVED);Тут появляется понятие дочерних пакетов. Дочерние пакеты в Ада:package M.M1 is ... //пакет m1 – расширяет m, он как-бы дописывается в негоВ этом случае у нас есть доступ даже к private-членам базового пакета, так как это дочерний пакет. Понастоящему защищенных методов в модульных языках нет.Объект базового класса всегда является объектом производного класса, но не наоборот.Множественное наследование влечет такую проблему:Base *pb = new Base;Derived *pd = new Derived;pb = pd;class X: public Y, public Z {x py;x *px = new X;py = px;pz = px;};В общем случае, множественное наследование влечет некоторые проблемы, поэтому его не используют вомногих ЯП.[lect30]Досрочный экзамен – 20.12 – П8а, первая параЭкзамен – 15.01 (с 12 часов)Множественное наследованиеТолько С++ допускает полное и неограниченное наследование.
Ограниченное наследование (одинсуперкласс, но есть много интерфейсов) допускают все современные языки. Если допускается реальноемножественное наследование, то допускаются разнообразные схемы, например «бриллиантовое»наследование или наследование от разных экземпляров одного класса. Тут возникают некоторые проблемы.Так как поиск неразрешенных имен производится параллельно в обоих предках, поэтому мы должныобязательно уточнять, из какого предка используется члены. Где может понадобиться такое наследование?Например, в обобщенных списках. В результате в этих контейнерах можно хранить произвольные ТД.Конечно, позже появились контейнеры-шаблоны, которые более эффективны. Такие сложные схемынаследования изучает специальный раздел математики – теория решеток (или теория категорий).
А какможно использовать «бриллиантовое» наследования? Например, в библиотеке <iostream.h>. Естьбазовый класс ios – это обертка над файловыми дескрипторами, из него наследуются классы istream иostream, а уже из них наследуется iostream. Как нужно реализовать такое наследование? Использоватьвиртуальное наследование:class A { ... };class X: public virtual A { ... };class Y: public virtual A { ... };class W: public X, public Y { ...