Анимация
JavaScript


Главная  Библионтека 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 [ 57 ] 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105

Относительный перерасход памяти может частично компенсироваться совместным хранением указателя и счетчика ссылок, как показано на рис. 7.3. Структура, изображенная на рис. 7.3, позволяет сократить размер интеллектуального указателя до размера обычного указателя, но за счет скорости доступа: объект, на который ссылается интеллектуальный указатель создает дополнительный уровень косвенной адресации. Это довольно значительный недостаток, поскольку обычно интеллектуальные указатели используются несколько раз, а создаются и уничтожаются - лишь однажды.

pointee

pRefCount

pointee

pRefCount

pointee

pRefCount

Объест

Рис. 7.2. Три интеллектуальных указателя, ссылающихся на один и тот же объект

ро1п1ее

pointee

SPImpI

pointee

pObL

refCount =3

Объект

Рис. 7.3. Альтернативная структура указателей с подсчетом ссылок

Эффективнее всего хранить счетчик ссылок в самом объекте, на который ссылается интеллектуальный указатель, как показано на рис. 7.4. Таким образом, объект кла-са SmartPtr будет иметь размер обычного указателя, и дополнительных затрат памяти не будет совсем. Этот прием называется внедренным подсчетом ссылок (intrusive reference counting), поскольку счетчик ссылок "внедряется" в объект, хотя семантиче-



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

pointee

pointee

pointee

Объект

refCount =3

Рис. 7.4. Внедренный подсчет ссылок

Обобщенный интеллектуальный указатель должен при первой возможности использовать внедренный подсчет ссылок, а обычную схему подсчета ссылок следует рассматривать в качестве приемлемой альтернативы. Для реализации обычной схемы подсчета ссылок очень удобно применять механизм распределения памяти для небольших объектов, описанный в главе 4. Он позволяет снижать дополнительные затраты, вызванные необходимостью хранить счетчик ссылок.

7.5.4. Связывание ссылок

На самом деле нет никакой необходимости подсчитывать интеллектуальные указатели, ссылающиеся на один и тот же объект. Нужно лишь определять, когда этот счетчик станет равным нулю. Это приводит к идее "списка владельцев" (ownership list), показанного на рис. 7.5, и стратегии связывания ссылок.

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

Структура дважды связанного списка идеально подходит для отслеживания ссылок. Использовать односвязный список невозможно, поскольку удаление элемента из такого списка занимает линейное время. Вектор применить нельзя, так как объекты класса SmartPtr не занимают смежные участки памяти (и на удаление элементов из вектора также тратится линейное время). Нам нужна структура, в которой удаление и вставка элементов, а также проверка заполненности занимали бы одинаковое время. Единственной структурой, удовлетворяющей этим требованиям, является дважды связанный список.

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

Механизм связывания ссылок описан Ристо Ланкиненом (Risto Lankinen) в форуме Usenet в ноябре 1995 года.



pointee

prev

next

pointee

prev

next

pointee

prev

next

Объект

Рис. 7.5. Связывание ссылок

Преимушество связывания ссылок перед их подсчетом проявляется в том, что при первом способе не требуется дополнительная память. Это делает стратегию более надежной. Создание интеллектуального указателя со связыванием ссылок всегда работает безотказно. Недостатком этой стратегии является тот факт, что при связывании необходимо больше памяти для регистрации (три указателя вместо одного плюс одно целое число). Кроме того, подсчет ссылок выполняется немного быстрее - при копировании интеллектуального указателя необходима только одна косвенная адресация и одна операция инкрементации. Управление списком также немного сложнее. В заключение отметим, что связывание ссылок следует применять только при недостатке динамической памяти. В противном случае предпочтительнее использовать подсчет ссылок.

Завершая обсуждение стратегий управления ссылками, обратим внимание на их существенный недостаток. Управление ссылками - с помощью подсчета или связывания - приводит к утечке ресурсов, так называемой циклической ссылке (cyclic reference). Представьте себе, что объект А содержит интеллектуальный указатель на объект В, и, наоборот, - в объекте в хранится интеллектуальный указатель на объект А. Эти два объекта образуют циклическую ссылку. Даже если вы не используете эти объекты, они сами друг друга используют. Стратегии управления ссылками не способны распознавать такие ситуации, и эти два объекта останутся в динамической памяти навсегда. Циклы могут распространяться на несколько объектов, создавая между ними связи, которые очень трудно отследить.

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

7.5.5. Разрушающее копирование

Разрушающее копирование (destmctive сору) - это именно то, о чем вы подумали: во время копирования оригинал уничтожается. Разрушающее копирование уничтожа-



0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 [ 57 ] 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105