Главная » Просмотр файлов » М. Бен-Ари - Языки программирования. Практический сравнительный анализ (2000)

М. Бен-Ари - Языки программирования. Практический сравнительный анализ (2000) (1160781), страница 41

Файл №1160781 М. Бен-Ари - Языки программирования. Практический сравнительный анализ (2000) (М. Бен-Ари - Языки программирования. Практический сравнительный анализ (2000)) 41 страницаМ. Бен-Ари - Языки программирования. Практический сравнительный анализ (2000) (1160781) страница 412019-09-19СтудИзба
Просмтор этого файла доступен только зарегистрированным пользователям. Но у нас супер быстрая регистрация: достаточно только электронной почты!

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

virtual void set_speed(int); // Заместить виртуальную подпрограмм private:

int reentry_speed;

};

Space_Plane_Data sp;

update(sp, 2000,30000);

файл, содержащий определение для update, не нужно перекомпилировать, даже если а) новая подпрограмма заместила set_speed и б) значение формаль­ного параметра d в update содержит дополнительное поле reentry_speed.

Когда используется динамический полиморфизм?

Давайте объявим базовый класс с виртуальной подпрограммой и обычной не­виртуальной подпрограммой и породим класс, который добавляет дополни­тельное поле и дает новые объявления для обеих подпрограмм:

class Base_Class {

private:

int Base_Field;

public:

virtual void virtual_proc();

void ordinary_proc();

};

class Derived_Class : public Base_Class {

private:

int Derived_Field;

public:

virtual void virtual_proc();

void ordnary_proc(); };

Затем объявим экземпляры классов в качестве переменных. Присваивание значения производного класса переменной из базового класса разрешено:

Base_Class Base_0bject;

Derived_Class Derived_Object;

if (...) Base_0bject = Derived_Object;

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

Более того, вызов подпрограммы (виртуальной или не виртуальной) одно­значный, и компилятор может использовать статическое связывание:

Base_0bject .virtual_proc();

Base_Object.ordinary_proc();

Derived_0bject.virtual_proc();

Derived_0bject.ordinary_proc();

Предположим, однако, что используется косвенность, и указатель на произ­водный класс присвоен указателю на базовый класс:

Base_Class* Base_Ptr = new Base_Class;

Derived_Class* Derived_Ptr = new Derived_Class;

if (...) Base_Ptr = Derived_Ptr;

В этом случае семантика другая, так как базовый указатель ссылается на пол­ный производный объект без каких-либо усечений (см. рис. 14.4). При реали­зации не возникает никаких проблем, потому что мы принимаем, что все ука­затели представляются одинаково независимо от указуемого типа.

Важно обратить внимание на то, что после присваивания указателя компи­лятор больше не имеет никакой информации относительно типа указуемого объекта. Таким образом, у него нет возможности привязать вызов

Base_Ptr- >virtual_proc();

к правильной подпрограмме, и следует выполнить динамическую диспетче­ризацию. Аналогичная ситуация возникает, когда используется ссылочный параметр, как было показано выше.

Эта ситуация может внести путаницу, так как программисты обычно не де­лают различия между переменной и указуемым объектом. После следующих операторов:

inti1 = 1;

int i2 = 2;

int *p1 = &i1; // p1 ссылается на i1

int *p2 = &i2; // p2 ссылается на i2

p1 = p2; // p1 также ссылается на i2

i1 = i2; // i1 имеет то же самое значение, что и i2

вы ожидаете, что i1 == i2 и *р1 ==*р2; это, конечно, правильно, пока типы в точности совпадают, но это неверно для присваивания производного класса базовому классу из-за усечения. При использовании наследования вы долж­ны помнить, что указуемый объект может иметь тип, отличный от типа указу­емого объекта в объявлении указателя.

Есть одна западня в семантике динамического полиморфизма языка C++: если вы посмотрите внимательно, то заметите, что обсуждение касалось дис­петчеризации, относящейся к замещенной виртуальной подпрограмме. Но в классе могут также быть и обычные подпрограммы, которые замещаются:

Base_Ptr = Derived_Ptr;

Base_Ptr->virtual_proc(); // Диспетчеризуется по указанному типу

Base_Ptr->ordinary_proc(); // Статическое связывание с базовым типом!!

Существует различие в семантике между двумя вызовами: вызов виртуальной подпрограммы диспетчеризуется во время выполнения в соответствии с ти­пом указуемого объекта, в данном случае Derived_Class; вызов обычной под­программы связывается статически во время компиляции в соответствии с типом указателя, ъ данном случае Base_Class. Это различие весьма сущест­венно, потому что изменение, которое состоит в замене невиртуальной под­программы на виртуальную подпрограмму или обратно, может вызвать ошиб­ки во всем семействе классов, полученных из базового.

Динамическая диспетчеризация в языке C++ рассчитана на вызовы вир­туальных подпрограмм, осуществляемые через указатель или ссылку.

Реализация

Ранее мы отмечали, что если подпрограмма не найдена в производном клас­се, то поиск делается в предшествующих классах, пока не будет найдено опре­деление подпрограммы. В случае статического связывания поиск можно де­лать во время компиляции: компилятор просматривает базовый класс произ­водного класса, затем его базовый класс, и так далее, пока не будет найдено соответствующее связывание подпрограммы. Затем для этой подпрограммы может компилироваться обычный вызов процедуры.

Если используются виртуальные подпрограммы, ситуация усложняется, потому что фактическая подпрограмма, которая должна быть вызвана, не из­вестна до времени выполнения. Обратите внимание, что, если виртуальная подпрограмма вызывается с объектом конкретного типа, в противополож­ность ссылке или указателю, то все еще может использоваться статическое связывание. С другой стороны, решение, какую именно подпрограмму следу­ет вызвать, основано на 1) имени подпрограммы и 2) классе объекта. Но первое известно во время компиляции, поэтому нам остается только смодели­ровать case-оператор по классам.

Обычно реализация выглядит немного иначе; для каждого класса с вирту­альными подпрограммами поддерживается таблица диспетчеризации (см. рис. 14.5). Каждое значение класса должно «иметь при себе» свой индекс для входа в таблицу диспетчеризации для порождающего семейства, в котором оно определено. Элементы таблицы диспетчеризации являются указателями на таблицы переходов; в каждой таблице переходов содержатся адреса входов в виртуальные подпрограммы. Обратите внимание, что два элемента таблицы переходов могут указывать на одну и ту же процедуру; это произойдет, когда класс не замещает виртуальную подпрограмму. На рисунке cls3 произведен из

cls2, который в свою очередь произведен из базового класса cls1. Здесь cls2 заместил р2, но не р1, в то время как cls3 заместил обе подпрограммы.

Когда встречается вызов диспетчеризуемой подпрограммы ptr->p1(), вы­полняется код наподобие приведенного ниже, где мы подразумеваем, что не­явный индекс — это первое поле указуемого объекта:

load RO.ptr Получить адрес объекта

load R1 ,(RO) Получить индекс указуемого объекта

load R2,&dispatch Получить адрес таблицы отправлений

add R2.R1 Вычислить адрес таблицы переходов

load R3,(R2) Получить адрес таблицы переходов

load R4,p1(R3) Получить адрес процедуры

call (R4) Вызвать процедуру, адрес которой находится в R4

Даже без последующей оптимизации затраты на время выполнения относи­тельно малы, и, что более важно, фиксированы, поэтому в большинстве прило­жений нет необходимости воздерживаться от использования динамического полиморфизма. Но все же издержки существуют и применять динамический полиморфизм следует только после тщательного анализа. Лучше избегать обеих крайностей: и чрезмерного использования динамического полимор­физма только потому, что это «хорошая идея», и отказа от него, потому что это «неэффективно».

Обратите внимание, что фиксированные затраты получаются благодаря тому, что динамический полиморфизм ограничен фиксированным набором классов, порожденных из базового класса (поэтому может использоваться таблица диспетчеризации фиксированного размера), и фиксированным набо­ром виртуальных функций, которые могут быть переопределены (поэтому размер каждой таблицы переходов также фиксирован). Значительным дости­жением языка C++ была демонстрация того, что динамический полимор­физм может быть реализован без неограниченного поиска во время выполнения.

14.5. Объектно-ориентированное программирование на языке Ada 95

В языке Ada 83 наличие пакетной конструкции обеспечивает полную поддержку инкапсуляции, а наличие производных типов частично обеспе­чивает наследование. Полного наследования нет, потому что, когда вы произ­водите новый тип, то можете добавлять только новые операции, но не новые компоненты данных. Кроме того, единственный полиморфизм — это стати­ческий полиморфизм вариантных записей. В языке Ada 95 поддерживается полное наследование за счет того, что программисту дается возможность рас­ширить запись производного типа. Чтобы обозначить, что родительский тип записи пригоден для наследования, его нужно объявить как теговый (tagged) тип записи:

package Airplane_Package is

type Airplane_Data is tagged

record

ID:String(1..80);

Speed: Integer range 0..1000;

Altitude: Integer range 0..100;

end record;

end Airplane_Package;

Этот тег аналогичен тегу в языке Pascal и дискриминанту в вариантных запи­сях языка Ada, где он используется для того, чтобы различать разные типы, производные друг из друга. В отличие от этих конструкций, тег теговой записи неявный, и программист не должен явно к нему обращаться. Заглядывая впе­ред, скажем, что этот неявный тег будет использоваться, чтобы диспетчери-зовать вызовы подпрограмм для динамического полиморфизма.

Чтобы создать абстрактный тип данных, тип должен быть объявлен как приватный и полное объявление типа дано в закрытой части:

package Airplane_Package is

type Airplane_Data is tagged private;

procedure Set_ID(A: in out Airplane_Data; S: in String);

function Get_ID(A: Airplane_Data) return String;

procedure Set_Speed(A: in out Airplane_Data; I: in Integer);

function Get_Speed(A: Airplane_Data) return Integer;

procedure Set_Altitude(A: in out Airplane_Data; I: in Integer);

function Get_Altitude(A: Airplane_Data) return Integer;

private

type Airplane_Data is tagged

record

ID:String(1..80);

Speed: Integer range 0..1000;

Altitude: Integer range 0.. 100;

end record;

end Airplane_Package;

Подпрограммы, определенные внутри спецификации пакета, содержащей объявление тегового типа (наряду со стандартными операциями на типе), на­зываются примитивными операциями, или операциями-примитивами (primitive operations) и являются подпрограммами, которые наследуются. Наследование выполняется за счет расширения (extending) тегового типа:

with Airplane_Package; use Airplane_Package;

package SST_Package is

type SST_Data is new Airplane_Data with

record

Mach: Float;

end record;

procedure Set_Speed(A: in out SST_Data; I: iri Integer);

function Get_Speed(A: SST_Data) return Integer;

end SST_Package;

Значения этого производного типа являются копиями значений родительско­го типа Airplane_Data вместе с (with) дополнительным полем записи Mach. Операции, определенные для этого типа, являются копиями элементарных подпрограмм; эти операции могут быть замещены. Конечно, для производно­го типа могут быть объявлены другие самостоятельные подпрограммы.

В языке Ada нет специального синтаксиса для вызова подпрограмм-примитивов:

A: Airplane_Data;

Set_Speed(A, 100);

С точки зрения синтаксиса объект А — это обычный параметр; И по его типу компилятор может решить, какую именно подпрограмму вызвать. Параметр называется управляющим, Потому что он управляет тем, какую подпрограмму выбрать. Управляющий параметр не обязан быть первым параметром, и их мо­жет быть несколько (при условии, что все они того же типа). Сравните это с языком C++, который использует специальный синтаксис, чтобы вы-звать подпрограмму, объявленную в классе:

C++

Airplane_Data а;

a.set_speed(100);

Объект а является отличимым получателем (distinguished receiver) сообщения set_speed. Отличимый получатель является неявным параметром, в данном случае обозначающим, что скорость (speed) будет установлена (set) для объ­екта а.

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

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

Список файлов книги

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