лекция 20 (1161115), страница 2

Файл №1161115 лекция 20 (Языки программирования (лекции) (2008)) 2 страницалекция 20 (1161115) страница 22019-09-19СтудИзба
Просмтор этого файла доступен только зарегистрированным пользователям. Но у нас супер быстрая регистрация: достаточно только электронной почты!

Текст из файла (страница 2)

Только переменные классового типа и формальные параметры имеют динамический тип. ClanWideType – реализован в виде ссылки.

A:T ' class; //так нельзя писать без присваивания. А сразу присвоить им какое-то значение можно.

Также мы не можем объявить переменную типа неограниченный массив, мы сразу должны приписать к ней какое-нибудь уточнение.

В нашей переписанной процедуре семантика вызова f() существенно меняется - f() вызывается в зависимости от динамического типа А.

P(Y); // f() for T

P(Y1); // f() for T1

Здесь наблюдается существенная разница: когда мы писали f() мы что-нибудь сказали, про то будет ли она динамически привязана к типу или нет? Нет, не сказали. Концептуально: виртуальность функции есть виртуальность её вызова. В яп есть специальные средства, чтобы снять виртуальность вызова.

В языках, в которых мы привязаны к одному объекту, мы виртуальность привязываем для всего типа (не для конкретного вызова). А в Аде можно по-другому: любая процедура связанная с тегированным типом данных может стать динамически привязанной. Все зависит от механизма ее вызова: если она вызывается для обычного объекта – никакой динамической привязки нет. Если для объекта из классового типа – тогда имеет место динамический вызов. А объекту классового типа может соответствовать объект любого конкретного типа, который принадлежит классу соответствующего типа (совпадает с Т, либо является производным). Мы рассматривали до этого привязку к одному объекту, однако в Аде такого нет, для примера рассмотрим пример:

T=>T1; W=>W1// получаются две параллельные и независимые иерархии

procedure F(X:T; Y:W);

procedure F(X:T1; Y:W);

procedure F(X:T; Y:W1);

procedure F(X:T1; Y:W1); // Тут возникает 4 варианта.

Представим некую процедуру:

PROCEDURE PP(A:T ' class; B: W ' class) is

begin

F(A,B);// в зависимости от динамических типов А и В будет вызвана одна из 4-х возможностей

end;

- это мультиметоды (В Си++, Си#, Дельфи, Оберон, Java – монометоды.)

Замечание: Страуструп говорил, что мультиметоды - это бывает полезно. Например, для пересечения геометрических фигур (для каждого типа фигуры мы должны написать свою процедуру):

р:Figure; //указатель на фигуру

q:Figure; //указатель на фигуру

p@q.Intercept(); //метод, который зависит от 2-х аргументов

Но в Си++ мультиметодов нет.

Продолжим рассмотрение Ады95:

Создатели Ады не только продублировали функциональность других ООЯП, но и принесли свои решения. Динамическая связность – свойство вызова.

p.f();// не является ДС, у нее аргументы классового широкого типа

Если у процедуры формальный параметр обычного типа (не класса, но тегированного типа), а фактический параметр классового типа, то процедура вызывается в зависимости от динамического типа своего фактического параметра.

F(A); // динамическое связывание

F(Y); //не динамическое связывание

При вызове РР – никакого ДС нет!

Свойство модульных языков.

  • В языке Оберон нельзя смоделировать приватные типы данных:

MODULE M;

....

TYPE T* = RECORD

X:INTEGER; //Х не экспортируется, становиться как бы приватным для тех процедур и функций, которые находятся извне модуля М

END;

END M;

Если мы в том же модуле выведем тип Т1:

TYPE T1=RECORD(T)

Y:INTEGER;

END;

Все процедуры, которые мы описываем здесь и которые привязаны к типу Т1 (неважно как – статически или динамически), они имеют доступ к переменной Х. Так, скрыться от процедур и функций в одном и том же модуле (Х и У – приватные для внешних, и публик для внутренних процедур и функций) Заметим, что в Си++ аналогов нет, а в Си# есть – protected internal. Нормальные серьёзные иерархии типов для Оберона можно создавать только в пределах модуля, и это ограничение мощности языка.

  • В Аде это ограничение было обойдено при помощи дочерних пакетов:

package M is

type T is tagged private;// С одной стороны Т –тегированный (корень в иерархии типов), а с другой

приватный, чтобы реализовать инкапсуляцию (одно из необходимых условий

ООЯ)

функции над Т; //могут быть ДС

private

type T is tagged record ... //обязаны описать Т как тегированный

end record;

end M;

Что видят все модули использующие М о структуре типа Т? Ничего.

use M;

X:T; //никакой доступ к функциям Т мы получить не можем.

type T1 is new T with record

... //в увидим только добавленные здесь члены, а из Т мы ничего не увидим

end record

Разрабатывать иерархии типов можно только в пределах одного пакета, что противоречит идеологии Ада. В Аде 95 вышли из этого при помощи концепции дочернего пакета:

package M1.M is //пакет М1 является дочерним для М: дочерний пакет является как бы продолжением своего

родителя

....

Все объекты М1 имеют полный доступ к объектам Т. Фактически, пакет М1 объявляет себя дружественным к Т. Если в Си++ у нас сам класс выбирал друзей, то есть, можно набиваться "дочерью" к чужому пакету.

package M1.M is

type T1 is new T with private;//все что выводим из Т делаем приватным, но при этом видим внутреннюю

структуру Т в нашем дочернем пакете

private

type T1 is new T with record

...

end record;

end M1.M;

Дочерние пакеты используются для того, чтобы выводить новые тд из приватных тд родительских пакетов. Фактически, в Аде 95 не осталось приватных членов, зато остались защищённые.

Сделаем несколько попутных замечаний по поводу наследования вообще.

    1. Во-первых, это запрещение наследования.

В языке Си++ нельзя запретить наследование. Мы, конечно, можем объявить все члены класса приватными, но запретить наследование нам никто не может.

В других языках, прежде всего в Java и Cи#, есть запрещение наследования - модификаторы:

1)Java: final

2)Cи#: sealed

  • class X

{

public final void f() {...}

}

Даже если мы наследуем X=>Y, то подменять эту функцию f() мы не можем.

  • Аналогично в Cи#:

public sealed class Path {...}; // если модификатор стоит перед именем класса – то из этого класса нельзя ничего

выводить

Sealed нужен из нескольких соображений - идейное (если ничего не нужно наследовать от этой библиотеки), безопасность (например, при проектировании библиотек авторизации), и третье (last but not least) - избавление от виртуальных вызовов (иногда из соображений эффективности снимается виртуальность вызова – актуально для Java, где все методы ДС).

public final class Y; {

f() {...}

}

Y y;

y.f(); // компилятор может снять в этом случае динамичность вызова, поскольку из У ничего быть выведено не может

Запечатанность класса говорит о том, что ДС можно снять, и работа с классом может стать более эффективной, но это крайняя мера.

2) Ещё одно замечание, по поводу видимости.

При наследовании идёт речь о новой области видимости. Пусть мы наследуем У от Х, тогда У добавляет новую часть и получается вложенная область видимости.

Умные фразы:

"Замещение имён идет по именам, а не по сигнатурам".

"Замещение имён не проходит через границы области видимости".

Представим:

class X:

int f(int); //во внешней области видимости

classY:

int f(double); //внутри области видимости

Из умных фраз следует, что когда для объекта У вызываем функцию f, то какая функция вызовется?

Y.f(1) // f(double)

Здесь речь идёт о неявных преобразованиях (В Аде, например, нет неявных преобразований, и это помогает). Мы сначала ищем в нашей области видимости точное соответствие и мы не находим его, так как мы смотрим только внутри нашей области видимости! А находим только приближенное соответствие. Это распространяется и на виртуальные и на невиртуальные функции.

class X:

int f(int); //во внешней области видимости

void f();

classY:

int f(double); //внутри области видимости

Y.f(); // ошибка! Сначала ищем имя, потом применяем к нему правила разрешения. f() не найдено в данной области видимости (найдено – но ни одно перекрытие не подходит).

Тоже действует и при множественном наследовании. Компилятор сначала ищет имя, а потом пытается применить правила разрешения.

То же самое в Си#:

class X:

int f(int) {..}

class Y:

int f(strings);

y.f(1)//ошибка

y.X::f(1) //правильный вызов для Си++

Однако в Си# такого нет, но там есть преобразования. Либо если нам хочется обеспечить доступ к закрытой функции (любая одноименная функция закрывает соответствующее имя из другой области видимости). Перекрытие имен по всех языках идет в пределах одной и той же области видимости, а класс образует область видимости.

class Y:

int f(int i) //перекрываем имя в той же области видимости

{ base f(i); }//делегируем соответствующий вызов наверх

Такое преобразование:

((X)y).f(1) - но здесь всё равно будут лишние проверки

На этом с темой о динамическом связывании закончим.

Глава 3: Абстрактные классы и интерфейсы

Напоминание: Абстрактные классы и абстрактные типы данных - две разные вещи.

Чем хорошо динамическое связывание? За чем нам нужны абстрактные классы?

Рассмотрим пример из геометрии:

Объявим класс "фигура" (если у двух объектов есть что-то общее, то всё общее вынесем в базовый класс):

class Figure {

int x, y; //точка привязки к экрану

virtual void Draw(bool erase); //применим к каждой фигуре если параметр true то стираем, иначе рисуем

void Move (int dx, int dy) { //меняем точку привязки

Draw(true);

x+=dx;

y+=dy;

Draw(false);

};

};

Метод move мы сейчас написать можем:

Draw(true);

x+=dx;

y+=dy;

Draw(false);

А вот метод Draw() зависит от фигуры и он обязан быть виртуальным.

class Point: public Figure {

public:

void Draw(bool erase) {...};//переопределяем метод

}

class Circle: ...... Draw() // и так для каждой фигуры

Figure *p;

p->Draw(false); // теперь мы знаем что это рисовании определенной фигуры

можно написать процедуру DrawAll(); если фигуры у нас в списке. Такая процедура будет идти по списку и зарисовывать фигуры. Все получается очень удобно. DrawAll() – мы можем ее скомпилировать, затем вывести из Point новый класс Rectangle, можно слинковать новый класс со старым кодом DrawAll(), которая в откомпилированном виде будет прекрасно работать с новым классом.

Проблема в том, что в таком виде программу не удастся собрать. Редактор связи выдаст ошибку – не определен метод Draw для класса Figure. Для конкретных фигур понятно как рисовать и стирать (рисовать цветом фона). Но как рисовать для вообще_любой_фигуры непонятно. Редактор связей требует от нас: раз мы везде пишем Figure, то теоретически может быть вызвано Draw для Figure. Но мы знаем, что такого не будет, так как класс Figure – фиктивный. Для таких целей введены абстрактные классы, так Figure – абстрактный(вершина в иерархии) – он нужен для того, чтобы из него выводили другие классы, в которых заменяли метод Draw.

Мы вынуждены писать в Draw для Figure некоторый код: exit(-1) + диагностика

Плохо, то что формально нет никаких препятствия вызова такой процедуры Draw, и появления объектов класса Figure, хотя по идеи их заводить вообще нельзя.

Вот для таких классов и введено понятие абстрактных классов в языке Си++. Этот класс служит только как вершина в дереве классов.

В языке Си++ есть понятие чисто виртуального метода ("pure virtual"). Такие чисто виртуальные методы мы и называем абстрактными методами. Чисто виртуальный метод – такой, у которого может не быть тела. И можно определить виртуальный метод:

virtual void Draw (bool erase) = 0; // это означает, что соответствующий ему класс является абстрактным, и в программе нельзя заводить переменные такого вида (можно заводить указатели и ссылки на них). В классах наследниках мы должны переопределить этот метод.

Figure f; //ошибка

Figure *f;//можно

f = new Point(1,0); // можно

f = new Figure(); // нельзя!

1 На любом языке модно проектировать объектно-ориентировано, если на нем моделируется отношение наследования и наличия функционального типа данных, чтобы можно было реализовывать динамическое связывание.

2 Например, когда базовый метод обрабатывает события. Есть две стратегии обработки: частичная и полная. При частичной мы частично обрабатываем событие, а потом вызываем обработчик события базового класса (нажатие на клавиши или мыши). Полная реакция – обработчик кнопки (выполняется какое-то действие и вызывать процедуру из базового класса нет нужды).

Характеристики

Тип файла
Документ
Размер
115,5 Kb
Тип материала
Высшее учебное заведение

Список файлов лекций

Свежие статьи
Популярно сейчас
Почему делать на заказ в разы дороже, чем купить готовую учебную работу на СтудИзбе? Наши учебные работы продаются каждый год, тогда как большинство заказов выполняются с нуля. Найдите подходящий учебный материал на СтудИзбе!
Ответы на популярные вопросы
Да! Наши авторы собирают и выкладывают те работы, которые сдаются в Вашем учебном заведении ежегодно и уже проверены преподавателями.
Да! У нас любой человек может выложить любую учебную работу и зарабатывать на её продажах! Но каждый учебный материал публикуется только после тщательной проверки администрацией.
Вернём деньги! А если быть более точными, то автору даётся немного времени на исправление, а если не исправит или выйдет время, то вернём деньги в полном объёме!
Да! На равне с готовыми студенческими работами у нас продаются услуги. Цены на услуги видны сразу, то есть Вам нужно только указать параметры и сразу можно оплачивать.
Отзывы студентов
Ставлю 10/10
Все нравится, очень удобный сайт, помогает в учебе. Кроме этого, можно заработать самому, выставляя готовые учебные материалы на продажу здесь. Рейтинги и отзывы на преподавателей очень помогают сориентироваться в начале нового семестра. Спасибо за такую функцию. Ставлю максимальную оценку.
Лучшая платформа для успешной сдачи сессии
Познакомился со СтудИзбой благодаря своему другу, очень нравится интерфейс, количество доступных файлов, цена, в общем, все прекрасно. Даже сам продаю какие-то свои работы.
Студизба ван лав ❤
Очень офигенный сайт для студентов. Много полезных учебных материалов. Пользуюсь студизбой с октября 2021 года. Серьёзных нареканий нет. Хотелось бы, что бы ввели подписочную модель и сделали материалы дешевле 300 рублей в рамках подписки бесплатными.
Отличный сайт
Лично меня всё устраивает - и покупка, и продажа; и цены, и возможность предпросмотра куска файла, и обилие бесплатных файлов (в подборках по авторам, читай, ВУЗам и факультетам). Есть определённые баги, но всё решаемо, да и администраторы реагируют в течение суток.
Маленький отзыв о большом помощнике!
Студизба спасает в те моменты, когда сроки горят, а работ накопилось достаточно. Довольно удобный сайт с простой навигацией и огромным количеством материалов.
Студ. Изба как крупнейший сборник работ для студентов
Тут дофига бывает всего полезного. Печально, что бывают предметы по которым даже одного бесплатного решения нет, но это скорее вопрос к студентам. В остальном всё здорово.
Спасательный островок
Если уже не успеваешь разобраться или застрял на каком-то задание поможет тебе быстро и недорого решить твою проблему.
Всё и так отлично
Всё очень удобно. Особенно круто, что есть система бонусов и можно выводить остатки денег. Очень много качественных бесплатных файлов.
Отзыв о системе "Студизба"
Отличная платформа для распространения работ, востребованных студентами. Хорошо налаженная и качественная работа сайта, огромная база заданий и аудитория.
Отличный помощник
Отличный сайт с кучей полезных файлов, позволяющий найти много методичек / учебников / отзывов о вузах и преподователях.
Отлично помогает студентам в любой момент для решения трудных и незамедлительных задач
Хотелось бы больше конкретной информации о преподавателях. А так в принципе хороший сайт, всегда им пользуюсь и ни разу не было желания прекратить. Хороший сайт для помощи студентам, удобный и приятный интерфейс. Из недостатков можно выделить только отсутствия небольшого количества файлов.
Спасибо за шикарный сайт
Великолепный сайт на котором студент за не большие деньги может найти помощь с дз, проектами курсовыми, лабораторными, а также узнать отзывы на преподавателей и бесплатно скачать пособия.
Популярные преподаватели
Добавляйте материалы
и зарабатывайте!
Продажи идут автоматически
6376
Авторов
на СтудИзбе
309
Средний доход
с одного платного файла
Обучение Подробнее