лекции (2007) (1160825), страница 13

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

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

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

При рассмотрении динамического связывания был рассмотрен пример с графическим редактором, который позволяет рисовать разные фигуры. Была введена определённая иерархия типов: определён класс "Figure", из него выведены классы "Circle", "Line", "Point" и т.д. При этом в классе "Figure" мы вводили виртуальную функцию отрисовки "Draw", которую потом замещали конкретными реализациями в наследуемых классах. Но помимо этого придётся писать реализацию "Draw" и для самого класса "Figure" (иначе, например, не получится полностью заполнить таблицу виртуальных методов для класса "Figure"). Но нельзя нарисовать то, о чём ничего пока неизвестно.

Так мы приходим к понятию абстрактного метода - метода, который по своей сути пока не может иметь какой-либо реализации и рассчитан на то, что в наследуемых классах эта реализация появится. Соответственно, классы, в которых есть абстрактные методы, называются абстрактными классами. Важно различать понятия "абстрактный класс" и "абстрактный тип данных": как уже говорилось, абстрактный класс - это класс, который содержит абстрактные методы; абстрактный тип данных - это тип, у которого скрыта вся структура и реализация, а доступен только функциональный интерфейс. Конечно, абстрактный класс может являться абстрактным типом данных, но такое бывает далеко не всегда; есть примеры абстрактных классов, не являющихся АТД, и, наоборот, примеры АТД, не являющихся абстрактными классами.

В языке C++ понятие абстрактного метода сводится к такому понятию, как "чистая виртуальная функция":

class Figure

{

...

virtual void Draw() = 0;

...

}

Такой необычный синтаксис объясняется тем, что создатель языка, Бьерн Страуструп, пытался сделать язык, максимально похожий на C, и поэтому не стал вводить новое ключевое слово. У чистой виртуальной функции в абстрактном классе не существует тела, но она должна подменяться в каждом производном классе. Тем не менее, чистую виртуальную функцию всё-таки можно вызвать в конструкторе абстрактного класса, который вызовется при создании объекта производного класса. Поэтому какая-то реализация у чистых виртуальных функций всё-таки есть, и она обычно пишется разработчиками компиляторов. Ещё одним методом вызова абстрактного метода является его явный вызов со снятием виртуальности ("Figure::Draw"). Конечно, вызов абстрактного метода с большой вероятностью будет ошибочен.

Объект абстрактного класса не может создаваться в программе (так сделано в подавляющем большинстве современных ЯП, исключение составляет язык Delphi). Тем не менее, можно завести ссылку на абстрактный класс (в рассматриваемом примере - "Figure* pf") и использовать её с объектами производных типов. В Delphi можно создавать объекты абстрактного класса (правда, компилятор скорее всего выдаст соответствующее предупреждение), но при попытке вызвать абстрактный метод будет порождено исключение (которое можно "поймать", используя механизм обработки исключений).

В языках C#, Java, Delphi и Ada для абстрактных классов и методов введено ключевое слово "abstract". Синтаксически это выглядит следующим образом:

1) В C# и Java:

public abstract class X

{

...

public abstract void f( );

...

}

2) В Delphi:

type Figure = class

...

procedure Draw; abstract;

...

end;

3) В Ada:

type T is abstract tagged record ... end record;

...

type T1 is abstract tagged null record;

..

procedure Draw(F : in Figure) is abstract;

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

class Set

{

...

virtual void Incl (T& X) = 0;

virtual void Excl (T& X) = 0;

virtual bool In (T& X) = 0;

...

}

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

class BSet : public Set, private Bitset {...}

class SListSet : public Set, private SList {...}

...

Плюс состоит в том, что можно легко менять реализацию, при этом не меняя сам интерфейс. Ещё один плюс такого подхода в том, что неизменность интерфейса позволяет не перекомпилировать модули, использующие этот интерфейс, при изменении его реализации:

Set* MakeSet() {return new SListSet();}

В данном примере для того, чтобы сменить реализацию, достаточно в функции MakeSet всего лишь заменить SListSet на BSet.

Во многие современные ЯП понятие интерфейса вошло в явном виде: это языки C#, Java, Delphi, Ada-2005. Синтаксически:

1) В C#:

interface Set

{

...

void Incl (T& X);

void Excl (T& X);

bool In (T& X);

...

}

2) В Java - точно так же, как в языке C#, плюс интерфейс может содержать набор констант (статических финальных переменных):

interface Set

{

...

void Incl (T& X);

void Excl (T& X);

bool In (T& X);

static final int i = 10;

...

}

3) в Delphi:

type T = interface (BaseInterface, ...)

...

end;

Кроме того, понятие интерфейса в определённом смысле сливает концепции наследования и абстрактных классов воедино.

1) В языке C# при наследовании может быть только один "настоящий класс" (имеется в виду, не полностью абстрактный), остальные могут быть только интерфейсами:

class X : Base, I1, I2, ... {...}

2) В Java - всё то же самое, отличается только синтаксис (введена конструкция "extends - implements"):

class X extends Base implements I1, I2, ..., In {...}

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

С помощью интерфейсов можно решить некоторые языковые проблемы. Например, в языке Java для глубокого копирования объектов необходимо вызывать метод Clone(). Но свойство копироваться - это не свойство объектов конкретного класса, этим свойством может обладать любой объект. Поэтому в Java существует стандартный интерфейс Cloneable, в который "как бы" входит функция Clone(), а класс Object, являющийся верхним в иерархии классов объектов, этот интерфейс реализует ("class Object implements Cloneable, ..."). Почему Clone() "как бы" входит в Cloneable? Потому, что Cloneable принято относить к интерфейсам-маркерам - интерфейсам, которые сами являются пустыми, но о которых компилятор знает какую-то дополнительную информацию (например, компилятор проверяет, чтобы в классе, реализующем Cloneable, была переопределена функция Clone() ).

Ещё один пример - специальный цикл "foreach" в языке C#. Если имеется контейнер C, содержащий данные типа T, то при использовании цикла "foreach (T i in C) {...}" переменная i будет последовательно проходить по всем объектам контейнера C. Другими словами, контейнер можно рассматривать двояко: с одной стороны, с ним можно работать, как с чем-то целым (например, сделать его копию), а с другой стороны, его можно рассматривать как некий массив с данными и индексировать как C[i]. В языке C++ для такой работы с контейнером достаточно просто перекрыть операцию индексирования, но в C# запрещено переопределять стандартные операции (как, впрочем и в Java, и в Delphi). Поэтому вводится такое понятие, как индексер ("enumerator"). Скажем, в C# в роли индексера может выступать функция "this (T x) {...}", возвращающая объект типа T из контейнера; если её перекрыть для какого-то типа T, то по этому типу можно будет индексировать контейнер. Здесь тоже применяются интерфейсы: существует стандартный интерфейс IEnumerator (в котором определены различные методы, вроде MoveNext, Current, Reset и т.д.), и интерфейс IEnumerable, состоящий из одного метода GetEnumerator(), возвращающего интерфейс IEnumerator. Таким образом, если класс реализует интерфейс IEnumerable, то для него можно будет вызвать метод GetEnumerator() и получить интерфейс IEnumerable, а значит, и использовать его методы.

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

Глава 5. Дополнительные вопросы.

В этой главе будут рассмотрены два вопроса: множественное наследование и RTTI (динамическая идентификация типов).

5.1. Множественное наследование.

Вопрос о том, необходимо ли в ЯП множественное наследование, очень спорный. Достаточно распространено мнение о том, что язык Smalltalk содержит всё, что нужно для объектно-ориентированного программирования (а раз в Smalltalk нет множественного наследования, то оно и не нужно).

Того же направления придерживался Брэд Кокс, создатель Objective C (первой объектно-ориентированной реализации C): он считал, что в языках, базирующихся на C, нельзя вводить множественное наследование, так как это приведёт к значительному усложнению языка. Надо отметить, что Objective C получился очень простым и понятным языком, при этом полностью обратно совместимым с языком C (то есть любая программа на языке C являлась программой на языке C++). Более того, Objective C предлагал объектно-ориентированное программирование в стиле Smalltalk (с отправкой сообщений, "message-oriented"), а также множество других удобных нововведений. Но Objective C немного опоздал со своим выходом: он вышел в 1986-м году, а в конце 1985-го уже состоялся первый коммерческий релиз C++ (развития языка "C with Classes") от создателя C, Бьёрна Страуструпа. Ниша уже была занята, и Objected C не получил широкой популярности.

Язык C++, наоборот, стал очень популярен и стал развиваться не только по логическим, но и по маркетинговым соображениям. На конец 80-х был запланирован выпуск второй версии C++ (если точно, то C++ 2.0 вышел в 1989-ом году). Но выпускать версию с новым индексом, имея только мелкие доработки, было маркетологически неправильно; требовалось ввести что-то новое и достаточно мощное. У Страуструпа было 2 варианта - ввести либо шаблоны, либо множественное наследование (реализация обоих механизмов сразу заняла бы значительное время). Страуструп выбрал именно множественное наследование. Шаблоны появились позднее, и их реализация до сих пор отличается в разных компиляторах, при этом почти нигде нет полной совместимости шаблонов со стандартом языка.

Синтаксически множественное наследование в C++ выглядит так:

class X: список баз с модификаторами доступа {...}

Основная проблема, возникающая при множественном наследовании - это проблема конфликта имен. В списке баз не может быть одинаковых классов, но в разных классах могут быть одноимённые функции. Например, класс X наследует от двух независимых классов Y и Z, в каждом из которых есть функция f(). Тогда возникает вопрос, какая из этих двух функций должна использоваться в классе X. Проблема решается с помощью механизма уточнения имён: необходимо явно указывать либо "Y::f()", либо "Z::f()".

Y Z

\ /

X

Аналогичная ситуация возникает, если классы Y и Z наследуются от X, а класс W наследуется от Y и Z:

X X

| |

Y Z

\ /

W

Здесь проблема решается так же: с помощью "Y::f()" и "Z::f()".

Ещё одна проблема связана с распределением памяти. Пусть в классе X есть виртуальная функция f(), в классе Y - виртуальная функция g(), а класс W наследует от X и Y. Тогда, если ничего не менять в механизме ТВМ, возникает следуюшая проблема: при распределении памяти для класса W часть, взятая из класса Y, наверняка будет иметь ненулевое смещение (скорее всего, сначала память будет выделена под X, потом под Y, и затем под W); тогда функции g() нельзя передавать this, поскольку она начнёт работать с объектом класса W как с объектом класса Y, что вызовет ошибку. Ситуация ещё больше осложняется при использовании виртуальных функций: если f() в классе X и g() в классе Y виртуальны, а в классе W описаны их замещения, то при использовании указателей на классы X или Y с объектами класса Z будут возникать ошибки (при вызовах f() и g() ). Они связаны с тем, как устроены таблицы виртуальных методов для разных классов. Выход - для каждого класса надо запоминать какое-то смещение и при необходимости добавлять его к this, а это дополнительные расходы на память. Обычно для реализации этой идеи служат слегка модифицированные таблицы виртуальных методов (это хорошо сочетается и с механизмом виртуальных функций). Получается, что за множественное наследование приходится "платить" даже в том случае, если оно не используется.

Есть ещё одна проблема связана с необходимостью так называемого "ромбовидного" наследования. Типичный пример - потоки ввода/вывода в C++:

ios - int fd;...

/ \

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

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

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

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