Р.У. Себеста - Основные копцепции языков программирования (2001) (1160794), страница 119
Текст из файла (страница 119)
Учет доступа подкласса к "скрытой" части родительского класса делает подкласс зависимым от этих деталей. Любое изменение в реализации родительского класса потребует повторной компиляции подкласса, н во многих случаях такое изменение приведет к модификации подкласса. Это аннулирует преимущество сокрытия информации от клиентов подкласса. С другой стороны, сокрытие реализации родительского класса от подклассов может привести к неэффективному выполнению экземпляров таких подклассов. Причина этого может заключаться в разной эффективности прямого доступа к структурам данных и доступа к ним с помощью операторов, определенных в родительском классе.
Например, рассмотрим класс, определяющий стек, и подкласс, которыЯ должен содержать операцию возврата второго от вершины элемента. Если язык программирования использует наследование реализации, то эту операцию можно описать как простой возврат элемента, позиция которого определяется вычитанием единицы из указателя на вершину стека. Однако, если разработчик языка выбрал наследование интерфейса. этот код должен выглядеть примерно так: Ьпс яесопс(() ( зпт СЕгяр - Сор(); рор(); Епс Гежр сеяц1С = сор(]; рцяп(сежр)г гесцгп севр геяц1с; 453 Глово 11. Поддержке объектно-ориентированного прогроммироаония . чевилно, что этот процесс медленнее, чем прямой доступ ко второму от вершины элеечту стека. Однако, если реализация стека изменилась, этот метод, возможно, также нов. ебз ется изменить. Наилучшее решение лля разработчика языка — предоставить разработчику про—.зччного обеспечения возможность использовать как наследование реализации, так и -зследование интерфейса, и позволить ему решить, ориентируясь по ситуации, какой из '-зэиантов предпочтительнее.
11.3.4. Проверка типов и полиморфизм В разделе 11.2 полиморфизм в объектно-ориентированной сфере определен как по-.льзование полнморфного указателя или ссылки для доступа к методу, имя которого зчешается в иерархии классов, определяющей объект, на который указывает этот указа-езь или ссылка. Полиморфная переменная — это тип родительского класса, а ролитель. ий класс, в свою очередь, определяет, как минимум, протокол метода, замещаемого в -"щгзводных классах. Полиморфная переменная может ссылаться на объекты родитель: .ого класса и производных классов, так что класс объекта, на который она ссылается, :.егда определяется статически.
Сообщения, передаваемые с помощью полиморфных - ременных. должны связываться с методами динамически. Проблема здесь заключается : определении момента, когда происходит проверка типов при этом связывании. Этот вопрос очень важен, поскольку он сравним с вопросом о природе языка про—.зимирования. Если строгое типизирование является одной нз основных целей разра.. ткн языка, как во многих современных языках программирования, эта проверка типов .з кна выполняться статически, что налагает некоторые серьезные ограничения на от-.
щения между полиморфными сообщениями и методами. Существуют два вида проверки типов, которую следует выполнять лля сообщения и . стола в строго типизированном языке: типы параметров сообщения сравниваются с ти-зми формальных параметров метода, а тип возвращаемого методом объекта — с ожиземым типом сообщения. Если эти типы должны в точности совпадать, то замешаюший ' етод должен иметь то же количество и те же типы параметров, а также тот же тип возя.ашаемого значения, что и замещаемый метод. Единственным исключением из этого -.
авила может быть разрешение совместимости в отношении операции присваивания между фактическими и формальными параметрами, а также между возвращаемым типом метода и ожидаемым типом сообщения. Очевидной альтернативой статической проверке типов является откладывание проверки ло тех пор, пока полиморфная переменная не будет использована для вызова метода. Как и в других ситуациях, этот тип динамической проверки типов более лорог и откладывает обнаружение ошибок несовместимости типов. 11.3.5.
Одиночное и множественное наследование Попытаемся ответить на вопрос: позволяет ли язык осуществлять множественное наследование (в дополнение в одиночному наследованию)? Цель множественного наследования — позволить новому классу наследовать от нескольких классов, описывающих разные абстракции. Например, на языке 5ача часто пишут аплеты, содержащие анимацию. Такая анимация часто протекает параллельно в различных частях аллета. Аплеты поддерживаются классом Лрр1ег, а параллельность обеспечивается классом Тпгеас1.
1 1.3. Вопросы розроботки объектно-ориентированных языков Для такого аллета может оказаться необходимым наследование переменных и методов от обоих классов. В разделе 11. ! 0.2 мы обсулим, как решается эта проблема в языке )а»а. Поскольку множественное наследование метода очень полезно, почему иногда разработчики не включают его в язык программирования? Причины этого разделяются на две категории: сложность и эффективность.
Дополнительная сложность порождается несколькими проблемами. Одна из них — конфликт имен. Например, если подкласс С наследует от обоих классов й и В, а классы й и В содержат наследуемую переменную волз, каким образом класс С сможет ссылаться на две различные переменные с именем вща? Такая же ситуапия возникает, если и класс й, и класс В являются производными от общего родительского класса Е. Этот случай называется бриллиантовым наследованием (б!ашот 1ппег1!апсе).
При этом классы й н В имеют наследуемые переменные из класса Е, и класс С наследует по две версии каждой из этих переменных (в предположении. что они являются наследуемыми) от классов й и В. Бриллиантовое наследование показано на рис. 1! .1. Рис. 11.1. При»ер брныиантового насзедования Вопрос эффективности является скорее теоретическим, чем практическим.
В языке С++, например. поддержка множественного наследования требует еше одной дополнительной операпии для каждого динамически связанного вызова метода (Бггоикгир, 1995). Несмотря на то что эта операпия выполняется лаже, если программа не использует множественного наследования, такой подхол можно назвать относительно недорогим. Использование множественного наследования значительно усложняет организапию программы. Применяя множественное наследование, трудно разработать классы, которые должны использоваться в качестве родительских. Полдержка систем, использующих множественное наследование. может быть более серьезной проблемой, поскольку множественное наследование приводит к более сложным зависимостям между классакки.
Некоторым люлям не ясно, что выгоды от множественного наследования стоят дополнительных усилий, затраченных на разработку и полдержку использующих его систем. 11.3.6. Размещение в памяти и удаление из памяти объектов Существует два вопроса разработки программ, связанные с размещением объектов в памяти и удалением из нее. Первый нз них связан с местом, в котором размещаются объекты.
Если повеление объектов аналогично поведению абстрактных типов данных, то их можно разместить гле угодно. Это означает, что они могут быть либо статически размещены компилятором, либо размещены как автоматические объекты в стеке времени выполнения, либо созданы в динамической памяти с помощью такого оператора или функ~ии, как пои. Преимушество их размещения в динамической памяти состоит в единообразии метода создания и доступа к ним через указатели или ссылки. Это упрощает операпию присваивания для объектов, которая выполняется только с помощью измене- 460 Глава 11. Поддержка объектно-ориентированного программирования иия значения указателя или ссылки и позволяет неявно дифференцировать ссылки на объекты, упрощая синтаксис доступа.
Вторая проблема связана с размещением объектов в динамической памяти. Вопрос состоит в том, является ли удаление объекта из памяти неявным или явным, или и тем. и другим. Если удаление объекта из памяти является неявным. требуется наличие такого неявного метода восстановления памяти, как счетчик ссылок или сборка мусора. Если размещение объекта в памяти может быть неявным, возникает вопрос, могут ли создаваться висячие указатели или ссылки? 3 3.3.7.
Динамическое и статическое связывание Как мы уже обсуждали, динамическое связывание сообщений с методами в иерархии наследования — существенная часть объектно-ориентированного программирования. Вопрос в следующем: являются лн все связывания сообщений с методами динамическими. В качестве альтернативы можно позволить пользователю самому опрелелять, быть ли конкретному связыванию динамическим нли статическим. Преимущество этого подхода состоит в том, что статическое связывание быстрее. Так что, если связывание не обязано быть динамическим, зачем платить за это замеллением работы программ? 11.4. Обзор языка ЗптаПФаПс Язык Баайга!к — характерный объектно-ориентированный язык программирования. В этом разделе описаны некоторые общие свойства языка Бщайгайь В разделе 1 !.5 рассматриваются отличительные особенности подмножества детального синтаксиса и семантики языка Бгпай!айь В разделе ! 1.6 описываются два примера полных программ на языке Баа!йайь В разделе 11.7 обсуждается несколько крупномасштабных особенностей языка Бщайсайс с точки зрения вопросов разработки программ, описанных в разделе 11.3.
3 з.4.3. Общие характеристики Программа на языке Бтай!айс целиком состоит из объектов, и понятие объекта в этом языке является поистине универсальным. Действительно, все, от таких простых вещей, как целая константа 2, до сложных систем обработки файлов, представляет собой объект. Будучи объектами, они обрабатываются единообразно. Все они имеют локальную память, присущие им возможности обработки данных, способность обмениваться сообщениями с другими объектами, а также возможность наследовать методы и переменные от предков. Сообщения могут быть параметризованы с помощью переменных, ссылающихся на объект. Ответы на сообщения имеют вид объектов и применяются лля возврата запрошенной информации или для подтверждения, что требуемая работа выполнена полностью. Все объекты языка Бта!йа!й размещаются в динамической памяти и адресуются с помощью ссылок, которые неявно дифференцируются.