p: POLYGON; r: RECTANGLE

...

create r; -- Создание объекта RECTANGLE.

p := r; -- Полиморфное присваивание.

p.add_vertex (...)

Так как объект r скрывается под сущностью p класса POLYGON, а add_vertex экспортируемый компонент POLYGON, то его вызов сущностью p корректен. В результате выполнения в прямоугольнике появится еще одна вершина, а значит, будет создан недопустимый объект.

Корректность систем и классов

Для обсуждения проблем ковариантности и скрытия потомком нам понадобится несколько новых терминов. Будем называть классово-корректной (class-valid) систему, удовлетворяющую трем правилам описания типов, приведенным в начале лекции. Напомним их: каждая сущность имеет свой тип; тип фактического аргумента должен быть совместимым с типом формального, аналогичная ситуация с присваиванием; вызываемый компонент должен быть объявлен в своем классе и экспортирован классу, содержащему вызов.

Система называется системно-корректной (system-valid), если при ее выполнении не происходит нарушения типов.

В идеале оба понятия должны совпадать. Однако мы уже видели, что классово-корректная система в условиях наследования, ковариантности и скрытия потомком может не быть системно-корректной. Назовем такую ошибку нарушением системной корректности (system validity error).

Практический аспект

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

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

Далее мы будем затрагивать весьма тонкие и не столь часто дающие о себе знать аспекты объектного подхода. Читая книгу впервые, вы можете пропустить оставшиеся разделы этой лекции. Если вы лишь недавно занялись вопросами ОО-технологии, то лучше усвоите этот материал после изучения лекций 1-11 курса 'Основы объектно-ориентированного проектирования', посвященной методологии наследования, и в особенности лекции 6 курса 'Основы объектно-ориентированного проектирования', посвященной методологии наследования.

Корректность систем: первое приближение

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

Контравариантность и безвариантность

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

Оригинальность языка C++ в том, что он использует стратегию безвариантности (novariance), не позволяя менять тип аргументов в переопределяемых подпрограммах! Если бы язык C++ был строго типизированным языком, его системной типов было бы трудно пользоваться. Простейшее решение проблемы в этом языке, как и обход иных ограничений C++ (скажем, отсутствия ограниченной универсальности), состоит в использовании кастинга - приведения типа, что позволяет полностью игнорировать имеющийся механизм типизации. Это решение не кажется привлекательным. Заметим, однако, что ряд предложений, обсуждаемых ниже, будет опираться на безвариантность, смысл которой придаст введение новых механизмов работы с типами взамен ковариантного переопределения.

Использование родовых параметров

Универсальность лежит в основе интересной идеи, впервые высказанной Францем Вебером (Franz Weber). Объявим класс SKIER1, ограничив универсализацию родового параметра классом ROOM:

class SKIER1 [G -> ROOM] feature

accommodation: G

accommodate (r: G) is ... require ... do accommodation := r end

end

Тогда класс GIRL1 будет наследником SKIER1 [GIRL_ROOM] и т. д. Тем же приемом, каким бы странным он не казался на первый взгляд, можно воспользоваться и при отсутствии параллельной иерархии: class SKIER [G -> SKIER] .

Этот подход позволяет решить проблему ковариантности. При любом использовании класса необходимо задать фактический родовой параметр ROOM или GIRL_ROOM, так что неверная комбинация просто становится невозможной. Язык становится безвариантным, а система полностью отвечает потребностям ковариантности благодаря родовым параметрам.

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

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату