возможность преобразования запутанных ситуаций в разумные и осмысленные идиомы программирования (например, стеки временных вызовов и сгенерированное компилятором занесение переменной в регистр не нуждаются в подсчете ссылок). Можно провести месяцы в поиске особых связей между переменными, содержащими явные указатели на интерфейс в программе и оптимизировать избыточные вызовы AddRef и Release , но поступать так было бы неосмотрительно. Выгода от удаления этих избыточных вызовов явно незначительна, так как даже в худшем случае, когда объект вызывается с расстояния более 8500 миль со средней скоростью передачи 14.4 кбит/сек, эти избыточные вызовы никогда не уйдут из вызывающего потока и нечасто требуют множество инструкций для выполнения.

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

А1. Когда ненулевой интерфейсный указатель записывается в локальную переменную.

А2. Когда вызываемый объект пишет ненулевой интерфейсный указатель в параметр [out] или [in, out] метода или функции.

A3. Когда вызываемый объект возвращает ненулевой интерфейсный указатель как физический результат (physical result) функции.

А4. Когда ненулевой интерфейсный указатель пишется в элемент данных объекта.

Некоторые типичные ситуации, требующие вызова метода Release :

R1. Перед перезаписью ненулевой локальной переменной или элемента данных.

R2. Перед тем как покинуть область действия ненулевой локальной переменной.

R3. Когда вызываемый объект перезаписывает параметр [in,out] метода или функции, начальное значение которых отлично от нуля. Заметим, что параметры [out] предполагаются нулевыми при вводе и никогда не могут освобождаться вызываемым объектом.

R4. Перед перезаписью ненулевого элемента данных объекта.

R5. Перед завершением работы деструктора объекта, имеющего в качестве элемента данных ненулевой интерфейсный указатель.

Типичная ситуация, к которой применимо правило о дополнительной информации, возникает при передаче указателей интерфейсов функциям как параметрам [in]:

S1. Когда при вызове функции или метода ненулевой интерфейсный указатель передается через [in] -параметр, вызов AddRef и Release, не требуются, так как время жизни временной переменной в стеке является строгим подмножеством времени жизни выражения, использованного для инициализации формального аргумента.

Эти десять руководящих принципов охватывают ситуации, снова и снова возникающие при программировании в СОМ, и было бы неплохо их запомнить.

Чтобы конкретизировать правила подсчета ссылок в СОМ, предположим, что имеется глобальная функция, которая возвращает объекту интерфейсный указатель:

void GetObject([out] IUnknown **ppUnk);

и что имеется другая глобальная функция, которая выполняет некую полезную работу над объектом:

void UseObject([in] IUnknown *pUnk);

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

void GetAndUse(/* [out] */ IUnknown ** ppUnkOut)

{ IUnknown *pUnk1 = 0, *pUnk2 = 0; *ppUnkOut =0;

// R3

// get pointers to one (or two) objects

// получаем указатели на один (или два) объекта

GetObject(&pUnk1);

//A2

GetObject(&pUnk2);

//A1

// set pUnk2 to point to first object

// устанавливаем pUnk2, чтобы указать на первый объект

if (pUnk2) pUnk2->Release():

//R1

if (pUnk2 = pUnk1) pUnk2->AddRef():

//A1

// pass pUnk2 to some other function

// передаем pUnk2 какой-нибудь другой функции

UseObject(pUnk2);

//S1

// return pUnk2 to caller using ppUnkOut parameter

// возвращаем pUnk2 вызывающему объекту, используя

// параметр ppUnkOut

if (*ppUnkOut = pUnk2) (*ppUnkOut)->AddRef();

// A2

// falling out of scope so clean up

// выходит за область действия и поэтому освобождаем

if (pUnk1) pUnkl->Release();

//R2

if (pUnk2) pUnk2->Release();

//R2

}

Важно отметить, что в вышеприведенном коде правило A2 применяется дважды, но по двум разным причинам. При вызове GetObject код выступает как вызывающий объект, а реализация GetObject является вызываемым объектом. Это означает, что реализация GetObject является ответственной за вызов AddRef через параметр [out]. При перезаписи памяти, на которую ссылается ppUnkOut, код выступает как вызываемый объект и корректно вызывает AddRef через интерфейсный указатель перед возвратом управления вызывающему объекту.

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

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату