Поделиться через


Агрегат

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

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

Решение заключается в том, что агрегирование требует явной реализации IUnknown во внутреннем объекте и делегировании методов IUnknown любого другого интерфейса к методам IUnknown внешнего объекта.

Создание агрегируемых объектов

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

Следующий фрагмент кода иллюстрирует правильную реализацию агрегатного объекта с помощью метода вложенных классов реализации интерфейсов:

// CSomeObject is an aggregable object that implements 
// IUnknown and ISomeInterface 
class CSomeObject : public IUnknown 
{ 
    private: 
        DWORD        m_cRef;         // Object reference count 
        IUnknown*    m_pUnkOuter;    // Controlling IUnknown, no AddRef 
 
        // Nested class to implement the ISomeInterface interface 
        class CImpSomeInterface : public ISomeInterface 
        { 
            friend class CSomeObject ; 
            private: 
                DWORD    m_cRef;    // Interface ref-count, for debugging 
                IUnknown*    m_pUnkOuter;    // Controlling IUnknown 
            public: 
                CImpSomeInterface() { m_cRef = 0;   }; 
                ~ CImpSomeInterface(void) {}; 
 
                // IUnknown members delegate to the outer unknown 
                // IUnknown members do not control lifetime of object 
                STDMETHODIMP     QueryInterface(REFIID riid, void** ppv) 
                {    return m_pUnkOuter->QueryInterface(riid,ppv);   }; 
 
                STDMETHODIMP_(DWORD) AddRef(void) 
                {    return m_pUnkOuter->AddRef();   }; 
 
                STDMETHODIMP_(DWORD) Release(void) 
                {    return m_pUnkOuter->Release();   }; 
 
                // ISomeInterface members 
                STDMETHODIMP SomeMethod(void) 
                {    return S_OK;   }; 
        } ; 
        CImpSomeInterface m_ImpSomeInterface ; 
    public: 
        CSomeObject(IUnknown * pUnkOuter) 
        { 
            m_cRef=0; 
            // No AddRef necessary if non-NULL as we're aggregated. 
            m_pUnkOuter=pUnkOuter; 
            m_ImpSomeInterface.m_pUnkOuter=pUnkOuter; 
        } ; 
        ~CSomeObject(void) {} ; 
 
        // Static member function for creating new instances (don't use 
        // new directly). Protects against outer objects asking for 
        // interfaces other than Iunknown. 
        static HRESULT Create(IUnknown* pUnkOuter, REFIID riid, void **ppv) 
        { 
            CSomeObject*        pObj; 
            if (pUnkOuter != NULL && riid != IID_IUnknown) 
                return CLASS_E_NOAGGREGATION; 
            pObj = new CSomeObject(pUnkOuter); 
            if (pObj == NULL) 
                return E_OUTOFMEMORY; 
            // Set up the right unknown for delegation (the non-
            // aggregation case) 
            if (pUnkOuter == NULL) 
            {
                pObj->m_pUnkOuter = (IUnknown*)pObj ; 
                pObj->m_ImpSomeInterface.m_pUnkOuter = (IUnknown*)pObj;
            }
            HRESULT hr; 
            if (FAILED(hr = pObj->QueryInterface(riid, (void**)ppv))) 
                delete pObj ; 
            return hr; 
        } 
 
        // Inner IUnknown members, non-delegating 
        // Inner QueryInterface only controls inner object 
        STDMETHODIMP QueryInterface(REFIID riid, void** ppv) 
        { 
            *ppv=NULL; 
            if (riid == IID_IUnknown) 
                *ppv=this; 
            if (riid == IID_ISomeInterface) 
                *ppv=&m_ImpSomeInterface; 
            if (NULL==*ppv) 
                return ResultFromScode(E_NOINTERFACE); 
            ((IUnknown*)*ppv)->AddRef(); 
            return NOERROR; 
        } ; 
        STDMETHODIMP_(DWORD) AddRef(void) 
        {    return ++m_cRef; }; 
        STDMETHODIMP_(DWORD) Release(void) 
        { 
            if (--m_cRef != 0) 
                return m_cRef; 
            delete this; 
            return 0; 
        }; 
}; 
 

Агрегирование объектов

При разработке агрегатного объекта применяются следующие правила:

  • При создании внутреннего объекта внешний объект должен явно запрашивать его IUnknown.

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

  • Внешний объект должен вызвать его управляемый метод IUnknownRelease, если он запрашивает указатель на любой из интерфейсов внутреннего объекта. Чтобы освободить этот указатель, внешний объект вызывает его контролируемый метод IUnknownAddRef , за которым следует release на указатель внутреннего объекта.

    // Obtaining inner object interface pointer 
    pUnkInner->QueryInterface(IID_ISomeInterface, &pISomeInterface); 
    pUnkOuter->Release(); 
    
    // Releasing inner object interface pointer 
    pUnkOuter->AddRef(); 
    pISomeInterface->Release(); 
    
    
  • Внешний объект не должен слепо делегировать запрос на любой нераспознанный интерфейс внутреннему объекту, если это поведение не является конкретно намерением внешнего объекта.

Содержит или делегирование