На этом шаге рассмотрим понятие индивидуальности объекта.
Хошафян (Khoshafian) и Коуплэнд (Copeland) предложили следующее определение индивидуальности. Индивидуальность — это свойство объекта, отличающее его от всех других объектов [Khoshafian, S., and Copeland, G. November 1986. Object Identity. SIGPLAN Notices vol. 21(11), p. 406.].
Они отмечают, что "в большинстве языков программирования и языков, ориентированных на работу с базами данных, для различения временных объектов используются имена переменных, что приводит к смешению адресуемости и индивидуальности. Большинство баз данных постоянные объекты различают по ключам, что приводит к смешению значения и индивидуальности". Невозможность отличить имя объекта от самого объекта является источником множества ошибок в объектно-ориентированном программировании.
Важность поддержки индивидуальности создаваемых объектов и легкость ее невозвратимой утери демонстрируется в следующем примере.
Рассмотрим класс, описывающий экранный объект. Это абстракция довольно типична для систем с графическим интерфейсом (GUI). Она является базовым классом для всех объектов, которые можно отображать в окне, и описывает поведение всех таких объектов. Клиенты обычно рисуют, выбирают и перемещают объекты, а также запрашивают их состояние и местоположение.
Предположим, что в программе создано несколько экземпляров класса DisplayItem, как показано на рис. 1.
Рис.1. Пример индивидуальности объекта
В частности, будем считать, что четыре экземпляра класса расположены в четырех разных местах памяти, имеющих имена item1, item2, item3, item4. Пусть item1 — имя объекта класса DisplayItem, а три других — указатели на объект класса DisplayItem, причем указатели item2 и item3 действительно указывают на разные объекты класса DisplayItem (так как их объявление сопровождается размещением объекта DisplayItem в памяти), а указатель item4 не ссылается на реальный объект.
Более того, объекты, на которые ссылаются указатели item2 и item3, являются безымянными, так что на них можно ссылаться только косвенно — с помощью разыменования указателей.
Уникальная индивидуальность (но не обязательно уникальное имя) каждого объекта сохраняется на протяжении всего периода его существования, даже если его состояние изменяется.
Попробуем переместить, например, объект item1. Мы можем обратиться к объекту item2, определить его местоположение и переместить объект item1 на то же самое место.
Точно так же, присвоив указателю item4 значение указателя item3, можно ссылаться на объект, на который ранее ссылался указатель item3, с помощью указателя item4. Используя указатель item4, этот объект можно переместить в новое место, например, X = 38, Y = 100. Результат показан на рис. 2.
Рис.2. Пример индивидуальности объекта
Как видим, объект item1 и объект, на который ссылается указатель item2, имеют одинаковые состояния, а указатели item3 и item4 ссылаются на один и то же объект. Обратите внимание на то, что мы говорим "объект, на который ссылается указатель item2", а не "объект item2". Первое выражение более точно, хотя иногда эти фразы используются как синонимы.
Несмотря на то что объект item1 и объект, на который ссылается указатель item2, имеют одинаковое состояние, они остаются разными объектами. Кроме того, состояние объекта, на который ссылается указатель item3, было изменено с помощью его нового косвенного имени item4. Эта ситуация называется структурным разделением (structural sharing) и означает, что объект может именоваться по-разному. Иначе говоря, объект может иметь псевдонимы. Структурное разделение порождает много проблем в объектно-ориентированном программировании. Трудность распознавания побочных эффектов при обращении к объекту через псевдонимы часто приводит к "утечкам памяти", нарушению правил доступа к памяти, и, что еще хуже, непредсказуемому изменению состояния. Например, если уничтожить объект, на который ссылается указатель item3, то значение указателя item4 окажется бессмысленным. Такая ситуация называется висячей ссылкой (gangling reference).
Рис.3. Пример утечки памяти
Рассмотрим рис. 3, иллюстрирующий результат изменения значения указателя item2 так, чтобы он ссылался на объект item1. Теперь указатель item2 ссылается на объект item1. К сожалению, при этом произошла утечка памяти — объект, на который первоначально ссылался указатель item2, больше не именуется ни прямо, ни косвенно, и его индивидуальность потеряна. В таких языках программирования, как Smalltalk и Java, память, выделенная для подобных объектов, уничтожается механизмом "сборки мусора". В языках типа C++ утерянная память не освобождается, пока не завершится программа, создавшая объект. Такие утечки памяти могут вызвать и простое неудобство, и крах, особенно если программа должна непрерывно работать длительное время.
На следующем шаге рассмотрим связи между объектами.