Aggregation: Outer Object (KOALAA.CPP)

You may have noticed that KoalaC passed a mysterious NULL pointer as the first argument to the CreateAnimal function. This argument is the means by which an outer object passes its outer unknown pointer to Animal to inform the object that it is being created as part of an aggregate. We'll see shortly what Animal does with this pointer. From KoalaA's perspective, it must pass its own IUnknown pointer to this function in order to create an aggregatable Animal object, and in compliance with the other aggregation rule, KoalaA must request an IUnknown pointer in return. Compare this with KoalaC, which asked for an IAnimal in return; remember that KoalaA cannot do this because Animal's IUnknown pointer is the only way to control Animal's lifetime properly.

Here, then, is KoalaA's code to create the aggregated Animal object in KoalaA's initialization:


BOOL CKoalaA::Init(void)
{
HRESULT hr;

m_pImpIKoala=new CImpIKoala_A(this);

if (NULL==m_pImpIKoala)
return FALSE;

hr=CreateAnimal(this, IID_IUnknown, (void **)&m_pIUnknown);

if (FAILED(hr))
return FALSE;

hr=m_pIUnknown->QueryInterface(IID_IAnimal, (void **)&m_pIAnimal);

if (FAILED(hr))
return FALSE;

m_cRef--;
return TRUE;
}

This initialization procedure shows not only KoalaA passing its IUnknown to Animal (the this sent to CreateAnimal) but also the way in which a 32-bit aggregate has to fix its reference count. In this sample, the QueryInterface call to retrieve IAnimal will bump KoalaC's reference count to 1, but it has to be 0 because we still have to provide any external pointers. Therefore, we simply decrement m_cRef without calling our own Release, which would destroy the object. If we had to call Release here for some reason, you would have to wrap that call in m_cRef++ and m_cRef--.

We must also use m_pIAnimal->Release in our destructor in the following manner, as stipulated by the rules, as well as safeguard CKoalaA::Release:


CKoalaA::~CKoalaA(void)
{
AddRef();
ReleaseInterface(m_pIAnimal);
m_pIAnimal=NULL; //Already released

ReleaseInterface(m_pIUnknown);
DeleteInterfaceImp(m_pImpIKoala);
return;
}

DWORD CKoalaA::Release(void)
{
if (0!=--m_cRef)
return m_cRef;

m_cRef++; //Artificial count preventing reentrancy
delete this;
return 0;
}

Because we're doing aggregation, KoalaA doesn't implement IAnimal itself; however, it does need to return its IAnimal pointer when asked for it in QueryInterface. This can be done in two ways: either return a cached pointer or pass the call to Animal's IUnknown::QueryInterface (which does not delegate). KoalaA demonstrates the latter:


STDMETHODIMP CKoalaA::QueryInterface(REFIID riid, PPVOID ppv)
{
§

//Alternatively, *ppv=m_pIAnimal; works for this case.
if (IID_IAnimal==riid)
return m_pIUnknown->QueryInterface(riid, ppv);

§
}

Both ways implement the proper QueryInterface behavior for KoalaA. KoalaA's only other concern is releasing the Animal object during KoalaA's own destruction, which simply involves a call to Animal's IUnknown::Release. This is, again, why KoalaA must ask for Animal's IUnknown during creation because a Release call to any other interface would be routed to KoalaA's IUnknown, leaving no way to free Animal to the wild open spaces.