// release it later in the object's destructor

// освобождаем здесь ссылку на внешний объект и

// НЕ ОСВОБОЖДАЕМ ее потом в деструкторе объекта

if (SUCCEEDED(hr)) m_pTruck->Release();

}

Этот способ работает, поскольку время жизни внутреннего объекта является точным подмножеством времени жизни внешнего объекта. Это означает, что m_pTruck будет теоретически всегда указывать на существующий объект. Конечно, если внешний объект реализовал ITruck как отделяемый интерфейс, то все предыдущее неверно, так как вызов Release уничтожит этот отделяемый интерфейс.

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

Outer::Outer(void)

{

++m_cRef;

// protect against delete this

// защищаем против удаления this

CoCreateInstance(CLSID_Inner, this, CLSCTX_ALL, IID_IUnknown, (void**)&m_pUnkInner);

–m_cRef;

// allow delete this

// позволяем удалить this }

Данная методика стабилизации предотвращает преждевременное разрушение, когда внутренний объект освобождает указатели, которые он, быть может, получил в свой код инициализации. Эта методика настолько общепринята, что большинство СОМ-оболочек программирования включают в себя явный метод перекрытия (overridable), который работает внутри области действия пары инкремент/декремент. В MFC (Microsoft Foundation Classes – библиотека базовых классов Microsoft) этот метод называется CreateAggregates, в ATL – FinalConstruct.

Поскольку показанные выше методики реализации агрегируемого объекта не требуют никаких дополнительных базовых классов, кроме классов C++, то альтернативная форма макроса IMPLEMENT_UNKNOWN может прозрачно реализовать раздвоенную реализацию IUnknown. Определение исходного класса:

class Car : public ICar

{

Car(void);

IMPLEMENT_UNKNOWN(Car)

BEGIN_INTERFACE_TABLE(Car)

IMPLEMENTS_INTERFACE(ICar)

IMPLEMENTS_INTERFACE(IVehicle)

END_INTERFACE()

// IVehicle methods

// методы IVehicle

STDMETHODIMP GetMaxSpeed(long *pn);

// ICar methods

// методы ICar

STDMETHODIMP Brake(void);

};

просто переводится в следующее:

class Car : public ICar

{

Car(void);

//indicate that aggregation is required

// показываем, что требуется агрегирование

IMPLEMENT_AGGREGATABLE_UNKNOWN(Car)

BEGIN_INTERFACE_TABLE(Car)

IMPLEMENTS_INTERFACE(ICar)

IMPLEMENTS_INTERFACE(IVehicle)

END_INTERFACE()

// IVehicle methods

// методы IVehicle

STDMETHODIMP GetMaxSpeed(long *pn);

// ICar methods

// методы ICar

STDMETHODIMP Brake(void);

};

Встроенное расширение макроса IMPLEMENT_AGGREGATABLE_UNKNOWN включено в код, приложенный к этой книге.

Включение

Не все классы способны к агрегированию. Для того чтобы выставить неагрегируемые классы как часть индивидуальности другого объекта, необходимо, чтобы внешние объекты явно передавали вызовы методов внутренним объектам. Эта технология СОМ часто называется включением (containment).

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

0

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

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