Лекция 20 (1160818), страница 2

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

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

Понятие интерфейса позволяет нам реализовать понятие рефлексии. Рефлексия – механизм языка, который позволяет из программы управлять поведением как компилятора, так и среды времени выполнения. Например, в языке C# есть такая интересная конструкция

foreach (T i in S){…}

Если S является контейнером с данными типа Т, то тогда переменная i последовательно пробегает все объекты S. В частности, массив – встроенный тип данных языка C# - по определению является контейнером. Поэтому вместо того, чтобы устраивать индексированный цикл, мы можем устроить такой цикл. Переход от предыдущего к следующему элементу в таком цикле реализуется эффективней чем адресация a[i]. Это конструкция языка.

Как в языке C#, так и в языке Delphi есть понятие индексера. Если класс является контейнером, т.е. содержит в себе данные других типов данных,

Cont A = new Cont;

то, в принципе, к такому контейнеру можно обращаться двояко: можно работать с ним как с целым

A = B;

и в то же время его можно индексировать

A[i]

В языке C#, как и в языках Delphi и Java нет понятия переопределения стандартных знаков операций. В C++, если мы хотим индексировать таким образом, можно переопределить операцию индексирования. Если в C#

this(T x){…}

Эта функция и называется индексером. У нее должен быть один параметр. Ее параметром выступает индексом. Например, мы можем индексировать по целому типу, по строковому типу (тогда это типичный пример ассоциативного контейнера). Эта функция возвращает какой-то элемент соответствующего типа. Естественно, можно перекрывать различные типы индексов. В результате мы можем писать

A[i]

где i относится к типу данных Т.

В интерфейсе IGetEnumeration есть метод

IEnumerator * GetEnumerator( );

Если реализуется такой интерфейс, то для этого класса можно вызвать метод GetEnumerator, который возвращает интерфейс IEnumerator. В интерфейсе IEnumerator есть методы типа

MoveNext

Current

Если компилятор в таком цикле видит объект класса, который поддерживает IGetEnumeration, то он делает цикл по всем элементам.

enum = S.GetEnumerator( );

поскольку S реализует соответствующий интерфейс. Далее enum применяется для того, чтобы ссылаться

i = enum.Current( );

в конце цикла делаем

enum.MoveNext( );

MoveNext возвращает ссылку на следующий объект, либо нулевую ссылку в случае, если перечисление закончилось. Естественно что в рамках конкретной структуры данных можно MoveNext реализовать более эффективно, чем в случае использования индексов. Реализация ряда интерфейсов приводит к тому, что компилятор начинает работать специфическим образом. Это типичный случай рефлексии, когда мы поведением компилятора управляем через конструкции языка.

Понятие интерфейса распространено достаточно широко. Такие модели распределенных объектов как COM Microsoft и CORBA в явном виде используют понятие интерфейса. Есть специальный язык IDL (Interface Description Language). COM и CORBA – достаточно разные реализации технологии распределенных объектов (т.е. объектов, которые могут общаться между собой по сети). Тем не менее что-то общее в этих моделях есть. Прежде всего то, что поведение объектов и в рамках COM, и в рамках CORBA определяется в терминах некоторого языка описания интерфейсов. Предложение IDL, в частности, - это и есть описание того, какие методы и какие свойства есть у того или иного объекта. Эти интерфейсы не зависят от языка программирования. Уже потом специальные компиляторы с интерфейсного языка генерируют привязки этих объектов, во-первых, к конкретным языкам программирования, и, во-вторых, к конкретному транспортному механизму. Существенно, что раз у нас есть такие модели, мы можем, в результате перейти на более языково не зависимый уровень. Можно и интерфейсы на Java, и интерфейсы на C#, и интерфейсы на Delphi описать с помощью IDL. Механизм реализации в COM и CORBA существенно различаются. Модель CORBA, с одной стороны, богаче чем COM. Она более объектно-ориентированная. Если в COM нет наследования, а есть чистая агрегация интерфейсов, то в CORBA есть и наследование и агрегация. Сейчас наблюдается тенденция перехода от CORBA, COM к платформе .NET.

С точки зрения реализации множественное наследование от интерфейсов не представляет сложности. Но при множественном наследовании остается проблема одинаковых имен. Каждая библиотека предоставляет весьма богатый набор интерфейсов. Поэтому довольно часто происходит пересечение в интерфейсах по именам. Когда мы сами разрабатываем интерфейс, это не так страшно - мы можем изменить соответствующее имя. В C# есть такая возможность как преимущественная реализация интерфейсов. Представим себе ситуацию

class X: I1, I2 {…}

interface I1{

void Execute( );

}

interface I2{

void Execute( );

}

X x;

x.Execute( );

Какой метод Execute будет выполняться, сколько реализаций метода Execute должно быть в классе Х, и как они между собой различаются? Например, Execute в одном случае – это запуск нового процесса, а в другом – запуск нового потока. В языке C# у нас есть возможность реализовать следующим образом

void I1.Execute( ){…}

void I2.Execute( ){…}

Теперь для выбора соответствующего метода нужно просто сделать приведение типа

((I1)x).Execute( );

((I2)x).Execute( );

В языке Java в таком случае предлагается выстроить нужную иерархию классов.

class XX implements I1{…};

class X extends XX implements I2{…};

ХХ реализует Execute для I1, Х расширяет ХХ и реализует I2. Теперь есть некоторая преференциальность.

x.Execute( );

означает вызов Execute для класса Х, и, значит, для интерфейса I2. Преобразование типа

XX(x).Execute( );

соответствует реализации Execute для интерфейса I1.

Глава 4. Динамическая идентификация типов.

RTTI – Run Time Type Identification. Для объектов ссылочных типов наряду со статическим типом есть динамический тип. Рассмотрим абстрактный базовый класс Figure, у которого есть метод отрисовки фигуры Draw( ), объявленный как чистая виртуальная функция для языка C++ (или соответственно абстрактная для других языков программирования). Мы пишем некоторое графическое приложение, у нас есть некоторая коллекция объектов Figure. У каждого конкретного класса есть своя реализация метода Draw. Программист может написать свою коллекцию, которая состоит из объектов типа Figure (Figure –указатель). Метод DrawAll( ) пробегает по всей коллекции и для каждого элемента коллекции вызывает метод Draw. Поскольку коллекция состоит из объектов типа Figure, то для них определен метод Draw. Приятно, что код процедуры DrawAll никак не зависит от конкретных классов – преимущество динамического связывания. Мы можем динамически пополнять систему без перетрансляции. Проблема в том, как писать контейнер. На всякий случай лучше пользоваться стандартными контейнерами. В языке C++ есть такая мощная вещь как шаблоны, и мы на основе контейнера делаем специализацию типа контейнера классом <Figure>. Поэтому в языке С++ вопросу о динамической идентификации типа в начале особого внимания не уделялось. Если в языке нет понятия шаблона можно писать свой контейнер для каждого типа данных. Все коллекции языка Java, все коллекции языка Delphi, все коллекции языка C# содержат объекты типа Object. Механизм шаблонов дает нам гарантию при компиляции, что неправильный объект (объект не типа Figure) не может попасть в коллекцию. Если коллекция просто над типом данных Object мы можем неправильно занести в нее объект. Для того, чтобы безопасно работать с коллекциями такого типа необходим механизм динамической идентификации типа. Т.е. если объект х не является объектом производного типа от класса Figure, то мы генерируем сообщение об ошибке во время выполнения. Нам нужен специальный механизм, который во время выполнения позволяет узнать принадлежит ли объект соответствующему типу. Даже в библиотеке языка С++ иногда возникает потребность в динамической идентификации типа. Такой механизм есть во всех ООЯП. Минимальный случай динамической идентификации типа мы видим в языке Оберон. Там есть 3 конструкции:

1) Проверка типа

2) Страж типа

3) Групповой страж – модификация стажа типа.

Будем обозначать Т<T1 в случае, если тип Т1 прямо или косвенно выводится из типа Т (Т1 как бы шире, добавлены новые свойства, новые поля). Проверка типа – это логическое выражение, которое имеет вид

t is T

где t – объект с динамическим типом, например, ссылка или указатель. Т – тип. Пусть Т1 – статический тип t. Только в случае, если T1<T, имеет смысл делать проверку. Если динамический тип t >= T, то проверка дает истину, в противном случае – ложь. Если Т1>=Т то t по правилам наследования одновременно является объектом типа Т и делать проверку смысла нет. В случае, если Т и Т1 находятся на разных ветвях иерархии, компилятор выдает ошибку. Если требуется проверка, компилятор поставляет код, который сравнивает динамический тип t и Т.

Страж типа имеет вид

t(T)

t – объект данных, Т – тип. Опять t – это либо ссылка, либо указатель (т.е. t должен иметь динамический тип).

type X = record

i:integer;

end;

type Y = record

j:integer;

end;

procedure P(VAR T: X);

Внутри процедуры мы можем писать T.i, но не имеем право писать T.j. Но мы можем писать T(Y).j. В этом месте компилятор выполняет проверку типа, и, если Т действительно является ссылкой на объект типа Y, то он разрешает операцию, если – нет, то он генерирует исключительную ситуацию. Это похоже на контролируемые преобразования указателей.

Поэтому программа на Обероне чаще всего имеет такой вид

if t is T then t(T). …

else

endif

В начале делается проверка типа, чтобы избежать исключений. Недостаток такой схемы в том, что при каждом обращении к t(T). … будет выполняться динамическая проверка типа, хотя мы ее уже сделали. Чтобы сэкономить на вычислениях вводится групповой страж типа, который внешне выглядит как оператор присоединения в языках Модула и Паскаль.

with t:T do

еnd

В нашем случае можно писать так

with T:Y do

T.i

T.j

end

Групповой страж работает так: один раз выполняется проверка типа. Поэтому соответствующая программа более эффективно может быть переписана в виде

if T is Y then

with T:Y do

else

endif

Правда при этом все равно выполняется 2 проверки типа. Нет никаких неконтролируемых преобразований. Это значит у нас нет возможности неправильно обратиться.

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

В С++ есть специальный класс, который называется typeinfo, в котором переопределен метод сравнения ==. Он содержит в себе некоторую информацию о типе. Например, имя соответствующего типа и какую-то дополнительную информацию. Есть специальная псевдофункция typeof(e), где е – выражение какого-то динамического типа, которая возвращает ссылку на typeinfo для динамического типа е. Если типы объектов совпадают, то typeof возвращает одну и ту же ссылку. Проверка

t is T

не эквивалентна

typeof(t)==typeof(T)

т.к. t is T истина, если динамический тип t >=T, а во втором случае сравнение только на равенство. Поэтому typeof используется достаточно редко и несет в себе не слишком много информации. В языке Оберон проверка типа t is T нужна для того, чтобы можно было безопасно обращаться со стражами типа. Страж типа похож на контролируемое преобразование указателей. Поэтому динамическая идентификация типа в языке С++ осуществляется с помощью контролируемого динамического преобразования указателей. Есть специальная конструкция

dinamic_cast<T>(e)

Т – некоторый тип данных (ссылочный или указательный, т.к. объекты других типов динамического типа не имеют), е – выражение (динамического типа). Если Т1 – статический тип е, то dinamic_cast имеет смысл только когда Т > T1. Если T не сопоставимо с T1, тогда компилятор выдает сообщение об ошибке, если Т<=T1, то тут и преобразовывать нечего. Т.е. ограничения те же самые, что и для проверки типа, и, как следствие, для стража типа. dinamic_cast очень похож на страж типа, но он одновременно работает и как страж типа, и как проверка типа. Если Т – указательный тип данных (тогда е тоже указатель), то в случае, если динамическая проверка типа истинна, выдается соответствующий указатель, преобразованный к типу данных Т, иначе NULL. Если Т – ссылочный тип, то в случае, если динамическая проверка типа истинна, выдается ссылка на Т, иначе генерируется исключительная ситуация, поскольку в С++ нет нулевой ссылки. Понятия группового стража нет. Оно и не нужно, поскольку сразу выдается преобразованный указатель. В современном стандарте языка С++ не рекомендуется использовать старые формы преобразований. По аналогии с dinamic_cast было введено еще 2 вида преобразования.

static_cast<T>(e)

reinterpret_cast<T>(e)

static_cast, как и dinamic_cast, применяется в случае, если Т – это ссылочный или указательный тип данных, совместимый с типом е. Только static_cast, в отличие от dinamic_cast, выполняет неконтролируемое (т.е. не безопасное) преобразование.

X * p; Y * p1;

p = (X *) p1;

- это то же самое, что

p = static_cast<X *>(p1);

Как правило это преобразование от базового типа к производному (но может быть и наоборот).

reinterpret_cast используется, когда типы Т и е не совместимы. Не выполняет никакого контроля.

По настоящему динамический тип будут иметь только те классы, в которых есть виртуальный метод, поскольку именно для этого случая важна проверка. Поэтому преобразование типов dinamic_cast допустимо только для классов с виртуальным методом, иначе выдается сообщение об ошибке.

Неявное преобразование, которое есть во всех языках, это преобразование от производного к базовому типу. Это единственное неявное преобразование в Java. В случае, если есть обратное преобразование, оно всегда явное и контролируемое.

((T1)x).Execute( );

в этом месте компилятор вставляет проверку, что х действительно реализует данный метод.

В C# и Java любые преобразования от базового типа к производному контролируемые. Это примерно то же, что dinamic_cast.

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

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

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

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