Как я уже говорил в главе 1, присвоение указателю не приводит к изменению значения, которое находится “на дальнем конце указателя”, на которое он указывает; это просто перенацеливание указателя.

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

Предположим, что мы реализовали класс Stack, описанный в главе 2, и рассмотрим следующий код:

Stack* myStackl = // ... Создание экземпляра Stack // и инициализация myStackl ...
Stack* myStack2 = myStackl;

Распространенное заблуждение — что присваивание myStack2 = myStackl создает новый, отдельный экземпляр, который дублирует myStackl. Это вовсе не так. Присваивание не создает новый экземпляр; оно просто приводит к тому, что my St ас к 2 указывает на тот же экземпляр, что и myStackl. Да, можно сделать новый экземпляр, который дублирует данный, но такая возможность не является данностью, а это действие не выполняется путем простого присваивания. (Как создаются экземпляры-дубликаты, описано в главе 10; см. описание протокола NSCopying и метода сору.)

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

Stack* myStackl - // ... Создание экземпляра Stack //и инициализация myStackl ...
Stack* myStack2 - myStackl;
[myStackl push: @"Hello"];
[myStackl push: @"World"];
NSString* s - [myStack2 pop] ;

Когда мы снимем элемент с вершины стека myStack2, значением переменной s окажется @"World", хотя мы ничего не помещали в стек myStack2; стек же myStackl после этого содержит только строку 0"Hello", хотя мы ничего не удаляли из стека myStackl. Это связано с тем, что мы внесли две строки в myStackl и удалили одну строку из myStack2, a myStackl является myStack2 — в том смысле, что это два указателя на один и тот же экземпляр стека. Это прекрасно до тех пор, пока вы понимаете и правильно используете это поведение.

Впрочем, иногда такое поведение является нежелательным. Если ваша программа имеет более чем одну ссылку на один и тот же экземпляр, можно получить удивительные результаты только потому, что (как и в предыдущем примере) этот экземпляр можно изменить с помощью одной ссылки в то время, когда владелец другой ссылки ничего подобного не ожидает. В реальной жизни проблемы такого рода могут возникнуть, в частности, потому, что экземпляры могут иметь переменные экземпляров, которые указывают на другие объекты, и эти указатели могут сохраняться до тех пор, пока существуют сами экземпляры. Предположим, у нас есть объект myOb j ect и мы передаем ему ссылку на объект нашего стека.

Stack* myStack - II ... Создание экземпляра Stack //и инициализация myStack ...
[myObject doSomethingWithThis: myStack]; // передача myStack
//в myObject

После этого кода myObject имеет указатель на тот же экземпляр, на который мы уже указываем с помощью myStack. Поэтому мы должны быть очень осторожны и внимательны. Объект myObject теперь может изменять наш myStack прямо у нас под носом! Более того, этот объект myObject может хранить свою ссылку на экземпляр стека (например, в переменной экземпляра) и изменить его позже — возможно, намного позже, и способом, который нас удивит. Такая ситуация может быть создана преднамеренно, но и в этом случае следует быть внимательным и точно понимать, что вы делаете и зачем (рис. 3.1).

Два экземпляра с указателями на один и тот же третий экземпляр

Рис 3.1 Два экземпляра с указателями на один и тот же третий экземпляр 


 

 

 

Добавить комментарий