лекция 21 (Языки программирования (лекции) (2008))

2019-09-19СтудИзба

Описание файла

Файл "лекция 21" внутри архива находится в папке "Языки программирования (лекции) (2008)". Документ из архива "Языки программирования (лекции) (2008)", который расположен в категории "". Всё это находится в предмете "языки программирования" из 7 семестр, которые можно найти в файловом архиве МГУ им. Ломоносова. Не смотря на прямую связь этого архива с МГУ им. Ломоносова, его также можно найти и в других разделах. .

Онлайн просмотр документа "лекция 21"

Текст из документа "лекция 21"

Языки программирования.

Лекция 21.

Глава 3: Абстрактные классы и интерфейсы (продолжение)

Абстрактный класс - это класс, в котором есть хоть одна чисто виртуальная функция (ЧВФ):

class X{

...

virtual void f() = 0;//ЧВФ; иногда говорят, что это абстрактные функции – они служат «заглушками» - для конкретных функций, которые появятся в конкретных классах

}

При этом для класса, в котором есть хотя бы одна виртуальная функция, программист не имеет право заводить объекты (они не имеют смысла – у них не определена хотя бы одна виртуальная функция). Мы имеем право не указывать тело ЧВФ, в то же время в стандарте сказано, что мы можем заводить тело чисто виртуальной функции. Но зачем, как мы сможем её вызвать? Ведь виртуальная функция всегда вызывается в зависимости от динамического типа, а динамический тип объекта никогда не может быть абстрактным классом:

X* px;

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

Как в такой ситуации мы можем вызвать чисто виртуальную функцию? Если через разрешение имени, то компилятор нас схватит за руку. Получается, что ЧВФ вообще вызывать не надо, зачем указывать тело?

Есть возможность когда мы вызываем тело ЧВФ (это нехорошо и неправильно, но можно). У абстрактного класса может быть конструктор, а есть такая вещь, что в конструкторах виртуальность вызова снимается:

classs Y{

virtual void g();

Y() {g();}//конструктор, в котором вызывается виртуальная функция

}

В конструкторах виртуальность вызова снимается, и в этом есть глубокий смысл. Пусть у нас из класса У есть производный класс Z. Как начинают работать конструкторы класса Z: в начале вызываются конструкторы баз, после этого вызываются конструкторы подобъектов, и после этого собственно конструктор класса Z. В начале вызывается конструктор базового класса (класса У), при этом части Z еще нет, и поэтому вызывать виртуальную функцию из класса Z нельзя (объекта Z еще нет). Когда работает конструктор класса Z, мы можем быть уверены, что ничего кроме класса У нет и поэтому вызывать виртуальную функцию из наследников не имеет смысла (пока мы еще не рождены, наследников у нас быть не может) – виртуальность вызова снимается., и если мы вызовем ЧВФ, то вызовется именно она (тело, которое генерируется по умолчанию, выдает сообщение об ошибке и заканчивается).

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

Тесно связано с понятием абстрактного класса понятие интерфейса. Это абстрактный класс, который доведен до абсолюта. Представим, что все соответствующие методы в классе являются ЧВФ, и никаких других методов у нас нет. На языковом уровне понятия интерфейса в Си++ нет, но появляется на технологическом. Интерфейс на Си++ - это класс, в котором нет никаких нестатических данных, все операции являются публичными и чисто виртуальными. Современные языки, в отличие от Си++, уже явно поддерживают интерфейсы на языковом уровне. Интерфейс это в чистом виде одни методы, хотя могу присутствовать статические члены (поскольку они к экземпляру класса никакого отношения не имеют).

Зачем нужны интерфейсы? Страуструп приводит пример с множествами. Должны присутствовать операции: объединение, пересечение, разность, добавление элемента, исключение, проверка на вхождение. Как сделать так, чтобы реализация множества не зависел от формы работы с множествами? Один из вариантов - реализовать интерфейс с 6 чисто виртуальными функциями.

Реализующий класс может выглядеть следующим образом (через однонаправленный список):

class SlistSet: public Set, public Slist {

virtual bool IsIn(T &x);

virtual void Excl(T &x);

......

}

Кроме этого SlistSet унаследует класс Slist (получается, что SlistSet у наследует класс реализацию и интерфейс Set). Операции над множество реализуем с помощью операций из SlistSet. Если нам не нравится класс SlistSet, то делаем другую реализацию Set (например, через Scale).

Можно сделать и так:

class SlistSet: public Set, private Slist {

virtual bool IsIn(T &x);

virtual void Excl(T &x);

......

}

Это означает, что работаем только с элементами из Set, а элементы из Slist нам недоступны. Чтобы программы не зависели от реализации, они должны работать через Set:

Set *pSet = set; // если set это указатель на объект класса SlistSet, то указатель на производный класс приводиться к указателю на базовый класс.

Все программы, работающие с Set, должны работать через указатель или ссылку на него. Нам нужна некая производящая функция, которая возвращает нам ссылку или указатель на Set, но использует при этом в качестве конструктора именно класс SlistSet. В результате если нам не нравится реализация в виде однонаправленного списка, то мы можем вместо реализующего класса Slist взять другой. Придется переделать только производящую функцию, которая генерирует объекты этого типа, а все остальное работает через интерфейс Set (нам даже не придется перетранслировать соответствующий модуль).

О минимизации расходов на перетрансляцию.

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

  • В Аде надо перетранслировать клиентские модули, если меняем структуру типа данных. Вспомним, приватные типы данных (прямо в спецификации пакета должна быть приватная часть для приватных тд):

package P is

type T is private;

private

type T is ... //структура типа Т здесь меняется

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

  • К какому типу отнести языки, основанные на понятии класса. Кажется, что к языку типа Ада – даже если мы меняем приватные члены, то мы все равно их меняем, и клиентские модули надо перетранслировать. С физической точки зрения: объявление класса в header файле, мы меняем объявление внутри header файла, мы вынуждены перетранслировать. Для некоторых проектов снижение затрат на перетрансляцию весьма серьезная проблема. А что делать, если мы часто меняем реализацию? Страуструп рекомендует работать в терминах интерфейсов.

Для нашего примера: программы, которые работают с header файлом, должны включать только спецификацию типа Set и прототип производящей функции, которая генерирует объекты конкретного класса Set. При смене реализации мы меняем только cpp-модуль, который и содержит в себе описание класса SlistSet, оно не содержится ни в каком header файле (либо содержится в приватном header файле, которые пользователи включать не должны). При смене типа Set c Slist на Map, то меняется соответствующий cpp-файл и текст производящей функции. Header не меняется – ничего перетранслировать не надо.

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

Интерфейсы хороши ещё тогда, когда они характеризуют некоторые свойства различных классов, например, свойство Serializable (возможность сохранения и загрузки из внешней памяти – реализация зависит от конкретного типа, поэтому такие функции изначально виртуальные и определяются для каждого конкретного класса). Имеет смысл оформлять все это не в виде класса, в виде интерфейса. Аналогично – Runnable(объекты, которые могут запускаться, содержат в себе некоторую исполняемую сущность).

Использование интерфейсов, даже в тривиальных примерах, подразумевает множественное наследование: класс использует интерфейс (Set) и должен реализовать унаследованные функции на базе некоторого конкретного которого (наследует класс Slist). Отметим, что в полном виде множественное наследование реализовано только в Си++ (из тех языков, которые мы рассматриваем).

В современных ООЯ понятия множественного наследования нет, наследование единичное. В самом чистом объектном языке SmallTalk множественного наследования нет. Однако, реализующий класс наследовать необязательно, его можно включать как некоторую переменную, мы говорили отношение наследования можно всегда заменять отношением включения.

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

Например, в Java:

class X entends Y //единичное наследование

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

class X entends Y implements I1, I2, I3 {...} //общая форма наследования

I1, I2, I3 - произвольный список интерфейсов; если часть extends Y отсутствует, то подразумевается extends Object.

Аналогично в Cи#: class X: Y, I1, I2, I3 {...}//общая форма наследования

Y, I1, I2, I3 // только одно из этих имен может быть именем класса, и находится оно может в любом месте

Интерфейсы в Java и Cи# немного отличаются. Синтаксически все выглядит очень похоже:

interface имя {

прототипы методов

....

final int red = 1;

}

  1. Модификаторов доступа нет, так как подразумевается, что все они по умолчанию public, virtual и не static.

  2. Ещё могут объявляться константы.

В Cи# внутри допускаются вложенные перечисления.

В Java внутри интерфейса допускается вложенное определение классов. В Java вложенные классы могут быть статическими (никакого отношения к внешнему классу не имеет, не считая локализации имён) и нестатическими (объекты и функции-члены этих классов являются продолжением внешнего класса). Любой объект вложенного нестатического класса имеет доступ к своему объемлющему супер-объекту, никаких разграничений с точки зрения доступа на внутренний класс не накладывается. В некоторых случаях эти внутренние классы могут быть полезными.

  • Обычный синтаксис создания нового класса:

new имя(...)

  • Анонимные классы:

    1. new имя_базового_класса{определения} // вводится новый анонимный класс, который производный от базового, с новыми методами и свойствами

    1. аналогично можно написать:

class Ano extends имя {...};

new Ano;

Анонимные классы удобно использовать, если нужно ввести один новый метод и использовать новый класс в одном месте один раз (например, если в Java нужно написать реакцию на нажатие кнопки, а понятия события у нас нет).

В наших интерфейсах даже если допускаются вложенные классы, то они обычные статические.

Если класс поддерживает интерфейс:

class X implements IFoo {

...//здесь мы должны реализовать все функции, описанные в IFoo

//интерфейсы служат, как заглушки

}

IFoo f = (IFoo)x; (если объект х поддерживает этот интерфейс).

С точки зрения реализации, что из себя представляет интерфейс? Интерфейс представляет из себя пару: this и указатель на ТВМ IFoo.

Вначале ТВМ Х это:

Y//виртуальные методы для класса У

I1//вм

I2//вм

I3//вм

Х//виртуальные методы, которые добавлены в классе X

В интерфейсе мы запоминаем ссылку на this (для того, чтобы получить ссылку на данные) и указатель на ТВМ IFoo. Интерфейсы нужны для того, чтобы не заботиться о конкретной сущности класса Х.

Например, если нам нужно реализовать загрузку и выгрузку объекта – мы делаем это используя одни и те же процедурами и функциями, не важно какой это объект.

Рассмотрим:

interface IFoo{

void Foo();

void Fа();

...

} //такое определение и для Java и для Cи# верно

Обратим внимание, что интерфейсы сами могут наследовать другие интерфейсы:

interface IFoo: IFoo1, IFoo2 {...

Java: interface IFoo extends

Cи#: IFoo1, IFoo2 {...

Проблемы при множественном наследовании

  1. При множественном наследовании возникает проблема конфликта имён.

Есть два подхода.

А) Пусть два интерфейса экспонируют метод Foo() (или более наглядный пример с функцией draw()). Идеология Java такая, что мы должны написать в реализации свою функцию с таким же именем, которая правильно поддерживала бы обе функции классов-родителей. Говорят, что интерфейс определяет некоторый контракт, и класс, который будет реализовывать этот интерфейс, говорит, что подчиняется некоторому контракту (соглашению). Интерфейс – набор соглашений, что мы должны поддерживать такие-то методы (если профили функций не совпадают, используется механизм перекрытий.)

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