Rationale for Ada 2005: Object oriented model
RUSTOPBACKNEXT
ENG |
7. Overriding and overloading
@ One of the key goals in the design of Ada was to encourage the writing of correct programs. It was intended that the structure, strong typing, and so on should ensure that many errors which are not detected by most languages until run time should be caught at compile time in Ada. Unfortunately the introduction of type extension and overriding in Ada 95 produced a situation where careless errors in subprogram profiles lead to errors which are awkward to detect. @ The Introduction described two typical examples. The first concerns the procedure Finalize. @ Consider
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Rationale for Ada 2005: Object oriented model
@ENGRUSTOPBACKNEXT7. Замена (overriding) и перегрузка (overloading)
@ Одна из главных заявленых целей Ады - поощрение написания правильных программ. Структура языка, строгий контроль типов, и так далее должны были гарантировать, что большинство ошибок, которые не обнаруживаются большинством языков, должны быть обнаружены во время компиляции. К сожалению, введение расширения типов и замены (overriding) в Аде 95 создало ситуацию, когда небрежные ошибки в конфигурации подпрограммы приводят к ошибкам, которые теперь невозможно обнаружить.
@ Введение описало два типичных примера. Первая проблема - процедура Finalize.
|
@ Мы неосторожно написали Finalise вместо Finalize. Это означает, что Finalize не переопределяется как ожидается и ожидаемое поведение при завершении объектов типа T не происходит.
@ В Аде 2005 мы можем указать префикс overriding в объявлении:
|
@ И теперь если мы неосторожно напишем Finalise, то это будет обнаружено при компиляции.
@ Подобные ошибки могут произойти в конфигурации. Если мы пишем:
|
@ тогда компилятор обнаружит, что у новой процедуры Op есть параметр типа String, а не Integer.
@ Однако, если мы действительно хотим добавить новую операцию тогда, мы можем написать:
|
@ Индикаторы замены могут также использоваться с абстрактными подпрограммами, нулевыми процедурами, переименованиями, реализациями, заглушками, телами и входами (мы будем иметь дело с входами в статье посвящённой задачам). Таким образом мы имеем:
|
@ Мы не должны применять индикатор замены и к спецификации процедуры и к телу, но если мы это делаем тогда, они естественно, не должны находиться в противоречии. Ожидается, что индикаторы замены будут обычно указываться в спецификациях, но они были бы уместными в случае тела без спецификации, как в примере Action в предыдущем разделе. Таким образом, мы могли бы иметь:
|
@ Индикаторы замены являются опциональными по двум причинам. Первое - просто для совместимости с Адой 95. Второе - некоторые проблемы с приватными типами и порождающими средствами.
@ Рассмотрим:
|
@ Теперь предположим, что у типа T нет операции Op. Тогда было бы неправильно написать:
|
@ потому что это противоречило бы информации, известной в частичном представлении.
@ Но предположим, что фактически оказывается, что в частной части тип NT фактически получен из TT (непосредственно унаследованный из T) и что у TT действительно есть операция Op.
|
@ В таком случае оказывается, что Op фактически заменяется в конце концов. В этом случае мы можем поместить индикатор overriding в тело Op, т.к. мы действительно знаем, что это замена.
@ Конечно мы не должны определять not overriding для Op в видимой части, потому что это не может бы быть истиной всегда (так как может случиться так, что у TT действительно есть Op). Однако, если мы поместим not overriding в частичное представление (что само по себе не является ошибкой), но если полное представление содержит not overriding оно, таким образом, гарантирует, что у TT нет Op.
@ Конечно, если у самого T есть Op тогда мы могли бы поместить индикатор overriding в видимую часть, так как мы знаем что это так.
@ Общее правило состоит в том, чтобы не лгать. Но правила являются немного разными для overriding и для not overriding. Для того, чтобы указать overriding это не должно быть неправдой в соответствующем пункте. Для указания not overriding это не должно входить в противоречие где-нибудь.
@ Эта асимметрия немного походит на предположение, что заключенный невинен, пока не оказался виновным. Мы иногда запускаем с целью, в котором операция, кажется, не отменяет, и затем позже мы находим, что она отменяет в конце концов. Но перемена никогда не случается - мы никогда не запускаем с целью, в котором она отменяет и затем позже обнаруживать, что этого не было. Таким образом, асимметрия реальна и выровнена.
@ Есть другие подобные, но более сложные проблемы с частными типами относительно неявных объявлений, где неявное объявление встречается намного позже и отменяет, но не имеет никакого физического присутствия, чтобы привести к зависанию индикатора. Было решено, что безусловно лучший подход к этим проблемам считать, что индикатор отмены является всегда опциональным. Мы не можем ожидать что найдутся все ошибки в программе через синтаксис и статическую проверку; главная цель здесь обеспечить простой способ найти большинство из них.
@ Подобные проблемы возникают и с порождающими средствами. Как обычно с порождающими средствами, правила проверяются в generic непосредственно и затем повторно проверяются после реализации (в этом случае для использования и в пределах видимой части и в пределах частной части спецификации). Рассмотрим:
|
@ Это должно быть незаконно, потому что у GT нет никакой операции Op. Конечно у фактического типа при реализации мог бы быть Op, но проверка должна пройти и в generic и в реализации.
@ С другой стороны высказывание not overriding позволено
|
@ Однако, в этом случае мы не можем проиллюстрировать GP с типом, у которого действительно есть операция Op, потому что он подвел бы при проверерке реализации. Таким образом в некотором смысле это налагает дальнейшее ограничение на generic. Если мы не хотим ввести это ограничение тогда, мы не должны указыватьь индикатор overriding процедуре Op для NT.
@ Другая ситуация возникает, когда generic формально наследуется:
|
@ В этом случае могло бы случиться так, что у типа T действительно есть операция Op, когда мы можем указать индикатор overriding.
@ Мы могли бы также попробовать
|
@ Но это является неправильным, потому что, хотя у GT должна быть операция, соответствующая Op как определено в формальном списке параметров, однако это не должна быть примитивная операция, и при этом это нельзя назвать Op, и таким образом это не унаследовано.
@ Нужно также заметить, что индикатор overriding может использоваться с нетеговыми типами, хотя он был представлен прежде всего, чтобы избежать проблем с диспетчеризацией операций. Рассмотрим:
|
|
@ Дело в том, что частичное представление не показывает, происходит отмена или нет - ни если это начиная с любой реализации должно быть приемлемо. Мы должны поэтому относится осторожно к отмене в частичном представлении. Это подобно частному расширению и generic случаям, обсуждаемым ранее. Вставка слова overriding была бы незаконна в обоих примерах, в то время как not overriding будет позволена только на втором (который сдержал бы реализацию как в предыдущих примерах). Снова, допустимо поместить индикатор overriding в тело "+", чтобы указать, отменяет ли это действительно.
@ Это также возможно для подпрограммы, чтобы быть примитивом для больше чем одного типа (это не может случиться для больше чем одного тегового типа, но это может случиться для нетеговых типов или одного тегового типа и некоторых нетеговых типов). Это могло тогда отменять для некоторых типов и не отменять для других. В таком случае это, как полагают, отменяет в целом, и любой индикатор должен отразить это.
@ Возможность наличия прагмы, которая провела бы в жизнь использование индикаторов отмены (так, чтобы они также не могли быть неосторожно опущены) была в конечном счете оставлена в значительной степени из-за частного типа и универсальной проблемы, которая сделала тему очень сложной.
@ Отметим рекомендуемую схему размещения, индикатор отмены должен быть помещен в строку перед спецификацией подпрограммы и выровнен с нею. Это не нарушаеть схему размещения спецификации.
@ Надеются, что программисты будут использовать индикаторы отмены свободно. Как упомянуто во Введении, они очень ценны для того, чтобы предотвратить противные ошибки во время обслуживания. Таким образом, если мы добавим дальнейший параметр для операции, такой как Op для корневого типа, и у всех расширений типа есть индикаторы отмены тогда, то компилятор сообщит об ошибке, если мы не изменим операторы всех производных типов правильно.
@ Мы теперь обращаемся к незначительному изменению в правилах отмены для функций с управлением результатами.
@ Читатель может напомнить общее правило в Аде 95, что функция, которая является примитивной операцией тегового типа и возвращает значение типа, должна всегда отменяться при расширении типа. Это потому что функция для расширенного типа должна создавать значение для дополнительных компонентов. Это правило иногда выражается утверждением, что функция являющейся абстрактной и так должна быть заменена, если расширенный тип конкретен. Раздражающая вещь этого правила в Аде 95 состоит в том, что это оно применяется, даже если нет никаких дополнительных компонентов.
@ Таким образом, рассмотрим универсальную версию пакета из Раздела 3:
|
@ Теперь предположим, что мы объявляем реализацию таким образом:
|
@ Это помещает тип Set со всеми его операциям в пакет My_Sets. Однако, по различным причинам мы могли бы желать иметь тип и его операции в текущей области видимости. Например, для простоты обозначения, чтобы мы не писали My_Sets.Set и My_Sets.Union и так далее. (Мы могли бы быть в ситуации, когда USE-выражения запрещены). Очевидный подход должен получить наш собственный тип в местном масштабе так, чтобы мы имели:
|
@ Другая ситуация, где мы, возможно, должны сделать это - ситуация когда мы желаем сделать тип Set частным:
|
@ Но это не работает нормально в Аде 95, так как у всех функций есть управляющие результаты и таким образом "go abstract" и поэтому должны быть заменены с обертками таким образом
|
@ Это - ясно ужасная неприятность. Ада 2005 заметно позволяет функциям быть наследованными при условии, что расширение явно нулевое (и что нет никакой новой дискриминантной части) и так никакая отмена не требуется. Это новое средство будет очень цениться пользователями новой контейнерной библиотеки в Аде 2005, у которого есть только этот стиль универсальных пакетов, которые экспортируют теговые типы.
@ Последняя тема, которая будет обсуждена это проблема с перегрузкой (overloading) и нетеговыми типами. Напомним, что понятие абстрактных подпрограмм было введено в Аду 95 в значительной степени с целью поддержки теговых типов. Однако, это может также использоваться с нетеговыми типами, если мы не хотим, чтобы операция была наследована. Это часто случается с типами, представляющими физические измерения.
@ Рассмотрим:
|
@ Эти типы наследуют различные нежелательные операции, такие как умножение длины длиной, чтобы дать длину, когда конечно мы хотим площадь. Мы можем исправить дело, отменяя их с абстрактными операциями следующим образом:
|
@ Мы также добавили функцию умножения двух длин возвращающую площадь. Так теперь у нас есть две абстрактные функции, умножающие две длины и возвращающие длину которые никогда не могут вызыватьс и одна функция правильно возвращяющая площадь.
@ Теперь предположим, что мы хотим распечатать значения этих типов. Мы могли бы объявить несколько функций, предоставляя строковое изображение таким образом:
|
@ И затем мы решаем написать:
|
@ Это не может компилироваться на Аде 95, так остаётся неоднозначность несмотря на то что Image и "*" перегружены. Проблема состоит в том, что, хотя функция "*" возвращяющая длину абстрактна, она однако все еще рассматривается как кандидат для перегрузки. Таким образом, мы не знаем, вызываем ли мы Image для длины или для области, потому что мы не знаем, который из "*" вовлечен.
@ Объявляя операцию с атрибутом abstaract мы в действительности не избавляется от операции вообще, это только препятствует её непосредственному вызову, но ее призрачные жизни являются неприятностью.
@ На Аде 2005 эта проблема решается по новому правилу, которое говорит, что "абстрактные подпрограммы недиспетчеризации игнорируютс во время разрешающей способности перегрузки". Таким образом, на Аде 2005 в приведённом примере abstract "*" игнорируется и нет никакой двусмысленности.
@ Отметим, что это правило не относится к диспетчеризации операций теговых типов, так как мы могли бы хотеть послать конкретной операции порожденного типа. Но это действительно относится к операциям типа всего класса.
2010-10-24 00:26:53
Информационные стойки, торговые стенды цены в Москве. . .