Сущность технологии СОМ. Библиотека программиста

ОглавлениеДобавить в закладки К обложке

{ } 

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

STDMETHODIMP(ULONG) AddRef(void)

{ return ++mcRef; }

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

STDMETHODIMP(ULONG) Release(void)

{

LONG res = -mcRef;

if (res == 0) delete this;

return res;

}

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

Заметим, что показанные реализации Addref и Release используют собственные операторы инкремента и декремента (увеличения и уменьшения на единицу). Для простой реализации это весьма разумно, так как СОМ не допускает более одного потока для обращения к объекту до тех пор, пока конструктор не обеспечит явный многопоточный доступ (почему и как конструктор сделает это, подробно описано в главе 5). В случае объектов, доступных в многопоточной среде, для автоматического подсчета ссылок следует использовать подпрограммы Win32 InterlockedIncrement/InterlockedDecrement:

STDMETHODIMP(ULONG) AddRef(void)

{

return InterlockedIncrement(&mcRef);

}

STDMETHODIMP(ULONG) Release(void)

{

LONG res = InterlockedDecrement(&mcRef);

if (res == 0) delete this; return res;

}

Этот код несколько менее эффективен, чем версии, использующие собственные операторы C++. Но, вообще говоря, разумнее использовать менее эффективные варианты InterlockedIncrement / InterlockedDecrement, так как известно, что они надежны во всех ситуациях и освобождают разработчика от необходимости сохранять две версии практически одинакового кода.

Показанные выше реализации AddRef и Release предполагают, что объект может размещаться только в динамически распределяемой области памяти (в «куче») с использованием С++-оператора new. В определении класса деструктор сделан защищенной операцией для обеспечения того, чтобы ни один экземпляр класса не был определен никаким другим способом. Однако иногда желательно иметь объекты, не размещенные в «куче». Для этих объектов вызов delete в последнем вызове Release был бы гибельным. Так как единственной причиной для того, чтобы объект в первую очередь поддерживал счетчик ссылок, была необходимость вызова delete this, допустимо оптимизировать счетчик ссылок для объектов, не содержащихся в динамически распределяемой области памяти:

STDMETHODIMP(ULONG) GlobalVar::AddRef(void)

{

return 2;

// any non-zero value is legal

// допустима любая ненулевая величина

}

STDMETHODIMP(ULONG) GlobalVar::Release (void)

{

return 1;

// any non-zero value is legal

// допустима любая ненулевая величина

}

Эта реализация использует тот факт, что результаты AddRef и Release служат только для сведения и не обязаны быть точными.

При наличии реализации AddRef и Release единственным еще не реализованным методом из IUnknown остается QueryInterface. Его реализации должны отслеживать иерархию типов объекта и использовать статические приведения типов для возврата правильного типа указателя для всех поддерживаемых интерфейсов. Для определения класса PugCat, рассмотренного ранее, следующий код является корректной реализацией QueryInterface : STDMETHODIMP


Логин
Пароль
Запомнить меня