Книга: Основы объектно-ориентированного программирования

Присоединение: две семантики - ссылок и значений

В этом разделе рассматривается специальная информация, и он может быть пропущен при первом чтении.

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

Присоединение

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

r (..., x: SOME_TYPE, ...)

Здесь сущность x это один из формальных аргументов r. Рассмотрим теперь некоторый вызов r в любой из двух возможных форм - квалифицированный или неквалифицированный вызов:

r (..., y, ...)
t.r (..., y, ...)

Выражение y является фактическим аргументом, передаваемым формальному аргументу x.

Выполнение r при любом из этих вызовов начинается с инициализации формальных аргументов значениями соответствующих фактических аргументов. Для простоты и согласованности правила, определяющие передачу аргументов, те же, что и правила присваивания. Другими словами, инициализация формального аргумента эквивалентна выполнению присваивания:

x := y

Это правило приводит к определению:

Определение: Присоединение

Присоединение y к x является результатом выполнения следующих двух операций:

Присваивания в форме x := y

Инициализации x при вызове подпрограммы, где x - формальный аргумент, а y - фактический аргумент вызова.

В обоих случаях x является целью присоединения, а y - источником.

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

Присоединение: ссылочное и копии

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

x := y

и соответствующей передачи аргументов состоит в том, что x получает значение ссылки y. Это иллюстрировалось несколькими примерами. Если значением y является void, то операция вместо присоединения сделает и x равным void; если y присоединен к объекту, то и x будет присоединен к этому же объекту.

Что происходит, когда типы x и y развернуты? Ссылочное присваивание не имеет смысла, а вот поверхностная форма копирования вполне возможна. Так и происходит. Рассмотрим объявления:

x, y: expanded SOME_CLASS

Присваивание x := y будет копировать каждое поле объекта, присоединенного к y, в соответствующие поля объекта, присоединенного к x, создавая тот же эффект, что и выполнение:

x.copy (y)

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

Семантика копирования для развернутых типов дает ожидаемый эффект для всех базисных типов, которые, как отмечалось выше все относятся к развернутым типам. Например, если m и n типа INTEGER, то мы ожидаем от присваивания m := n, (или от соответствующей передачи аргументов) копирования значения n в m.

Проведенный анализ применим и к связанной с присваиванием операции эквивалентности. Рассмотрим булевы выражения: x = y и x /= y. Для x и y ссылочных типов, как уже отмечалось, истинность первого выражения (ложность второго) достигается только тогда, когда источник и цель оба имеют значение void или оба присоединены к одному и тому же объекту. Для развернутых x и y, такая семантика неприемлема, - здесь действует другая семантика, основанная на последовательном сравнении значений соответствующих полей, так что в этом случае выражение x = y имеет то же значение, что и equal (x, y).

Разрешается, как мы увидим позже при обсуждении наследования, изменить семантику equal для придания специального смысла эквивалентности экземпляров некоторого класса. Это никак не отразится на операции эквивалентности =, которая по соображениям безопасности и простоты всегда имеет смысл оригинальной функции standard_equal.

Правило присваивания и сравнения обобщается в следующем замечании.

Присоединение y к x означает копирование объекта x, если x и y принадлежат развернутым типам. Это ссылочное присоединение, если x и y ссылочного типа. Аналогично, тесты: x=y и x/=y означают сравнение объектов для x и y развернутых типов; это ссылочное сравнение, если x и y ссылочного типа.

Гибридное присоединение

В рассматриваемых до сих пор случаях источник и цель принадлежали одной категории - оба развернутого или ссылочного типа. Что если они из разных категорий?

Вначале рассмотрим ситуацию, когда в присваивании x := y цель x развернутого типа, а источник y - ссылочного типа. Единственно приемлемой в этом случае является семантика копирования: копирование полей объекта, присоединенного к y, в поля объекта, присоединенного к x. Все хорошо, если y не void в период выполнения. Если y - void, то результатом будет включение исключения. (Исключения изучаются в лекции 12)

Для развернутого x тест x = Void не является причиной появления исключительной ситуации; он просто дает значение false. Но нет приемлемой семантики для присваивания x := Void, так что всякая подобная попытка приводит к появлению исключения.

Рассмотрим теперь другой случай присваивания: x := y, где x ссылочного типа, а y - развернутого. Тогда в период выполнения y всегда присоединен к объекту, который мы можем назвать OY, и присоединение также должно присоединить x к объекту. Казалось бы, что можно присоединить x непосредственно к OY. Однако это привело бы к созданию ссылки на подобъект, а подобные ссылки запрещены нашими правилами. Поэтому правильной стратегией является клонирование источника OY и присоединение x к созданной копии. Рассмотрим пример:

class C feature
...
end
class COMPOSITE2 feature
x: C
y: expanded C
reattach is
do x := y end
end

При вызове компонента reattach в результате присваивания x будет присоединен к объекту, являющемуся клоном объекта y.

Следующая таблица обобщает семантику присоединения изученных случаев:

Тип цели x Тип источника y
Ссылочный Развернутый
Ссылочный Ссылочное присоединение Клонирование: эффект x := clone(y)
Развернутый Копирование: эффект x.copy(y) Ошибка, если y - void Копирование: эффект x.copy(y)

Таблица 8.1. Эффект присоединения x:=y

Проверка эквивалентности

Семантика операций, проверяющих эквивалентность (= и /=) должна быть совместимой с семантикой присваивания. Наряду с операцией = можно использовать и equal. Какую из этих операций следует применять, зависит от обстоятельств.

[x]. (E1) Если x и y - ссылки, их можно тестировать как на ссылочную эквивалентность, так и на объектную эквивалентность при условии, что ссылки не void. Мы определили операцию x = y, как обозначающую ссылочную эквивалентность в этом случае. Функция equal, введенная для проверки объектной эквивалентности, дополнена и применима, когда x или y - void.

[x]. (E2) Если x и y - развернутого типа, единственный смысл имеет объектное сравнение.

[x]. (E3) Если x - ссылка, y - развернутого типа, объектное сравнение - единственно возможный смысл операции и в данном случае. Сравнение расширяется, допуская случай, когда x - void, возвращая значение false в этой ситуации, поскольку y не может быть void.

Этот анализ дает желаемую интерпретацию равенства = во всех случаях. Для объектного сравнения всегда доступна функция equal, расширенная на случаи, когда один или оба операнда принимают значение void. Следующая таблица подводит итог семантике сравнения:

Тип цели x Тип источника y
Ссылочный Развернутый
Ссылочный Ссылочное сравнение equal(x,y) объектное сравнение, если x не void, иначе - false
Развернутый equal(x,y) объектное сравнение, если y не void, иначе - false equal(x,y) объектное сравнение

Таблица 8.2.Семантика сравнения x=y

Сравнение таблиц 8.1 и 8.2 показывает совместимость присваивания и операций сравнения в упоминавшемся уже смысле. Напомним, в частности, что equal (x, y) будет истинно после выполнения x := clone (y) или x. copy (y).

Обсуждаемые проблемы возникают во всех языках, включающих ссылки и указатели, таких как Pascal, Ada, Modula-2, C, Lisp и другие. Они особенно актуальны для ОО-языков, в которых все создаваемые пользователем типы являются ссылочными. В дополнение к причинам, объясняемых в разделе обсуждения, в синтаксисе явно не отражается факт представления объектов ссылками, так что следует быть особо внимательными при проверке эквивалентности объектов.

Оглавление книги


Генерация: 1.143. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз