COM オブジェクトの有効期間の管理

オブジェクトが作成されると、システムによって、必要なメモリー リソースが割り当てられます。オブジェクトが必要なくなった場合は、破棄する必要があります。システムで別の目的にメモリーを使用できます。C++ オブジェクトでは、new 演算子と delete 演算子を使用して、オブジェクトの有効期間を直接制御できます。COM では、オブジェクトの作成や破棄を直接行うことはできません。これは、同じオブジェクトが複数のアプリケーションで使用される可能性があるためです。これらのアプリケーションのいずれかがオブジェクトを破棄することになっている場合に、その他のアプリケーションが失敗する可能性があります。代わりに、COM では、リファレンス カウントのシステムを使用してオブジェクトの有効期間を制御します。

オブジェクトのリファレンス カウントは、そのインターフェイスのうちの 1 つが要求された回数です。インターフェイスが要求されるたびに、リファレンス カウントがインクリメントされます。そのインターフェイスが必要なくなると、アプリケーションでインターフェイスが解放され、リファレンス カウントがデクリメントされます。リファレンス カウントが 0 より大きい間は、オブジェクトはメモリー内に残ります。リファレンス カウントが 0 になると、オブジェクトによってそれ自体が破棄されます。オブジェクトのリファレンス カウントについて知らなければならないことは何もありません。オブジェクトのインターフェイスを適切に取得および解放している限り、オブジェクトの有効期間は適切となります。

リファレンス カウントを適切に扱うことは、COM プログラミングにおいて非常に重要です。これに失敗すると、簡単にメモリー リークが発生します。COM プログラマに最もよくある誤りの 1 つは、インターフェイスの解放の失敗です。これが起こると、リファレンス カウントが 0 になることがなくなり、オブジェクトが無期限にメモリー内に残ります。

リファレンス カウントのインクリメントとデクリメント

新しいインターフェイス ポインターを取得するたびに、IUnknown::AddRef を呼び出すことによってリファレンス カウントがインクリメントされなければなりません。ただし、通常は、アプリケーションでこのメソッドを呼び出す必要はありません。オブジェクト作成メソッドを呼び出すか IUnknown::QueryInterface を呼び出すことによってインターフェイス ポインターを取得する場合は、オブジェクトによって自動的にリファレンス カウントがインクリメントされます。ただし、既存のポインターをコピーするなど、その他の方法でインターフェイス ポインターを作成する場合は、明示的に IUnknown::AddRef を呼び出す必要があります。そうしないと、まだそのポインターのコピーを使用する必要がある場合でも、元のインターフェイス ポインターを解放するときに、オブジェクトが破棄される可能性があります。

リファレンス カウントがインクリメントされたかどうかに関係なく、すべてのインターフェイス ポインターを解放する必要があります。インターフェイス ポインターが必要なくなったときは、IUnknown::Release を呼び出してリファレンス カウントをデクリメントします。一般的な方法では、すべてのインターフェイス ポインターを NULL に初期化し、後でそれらを解放するときに再度 NULL に設定して戻します。この規則により、クリーンアップ コードですべてのインターフェイス ポインターをテストできるようになります。NULL でない場合はそれらはまだアクティブなため、アプリケーションを終了する前に解放する必要があります。

次のコードは、リファレンス カウントの扱い方を示すために、COM インターフェイスの使用 で説明したサンプルを拡張したものです。

IDirectSoundBuffer9* pDSBPrimary = NULL;
IDirectSound3DListener9* pDSListener = NULL;
IDirectSound3DListener9* pDSListener2 = NULL;

...

//Create the object and obtain an additional interface.
//The object increments the reference count.

if(FAILED(hr = g_pDS->CreateSoundBuffer( &dsbd, &pDSBPrimary, NULL )))
  return hr;

if(FAILED(hr=pDSBPrimary->QueryInterface(IID_IDirectSound3DListener9,
                                        (LPVOID *)&pDSListener)))
  return hr;

//Make a copy of the IDirectSound3DListener9 interface pointer.
//Call AddRef to increment the reference count and to ensure that
//the object is not destroyed prematurely.

pDSListener2 = pDSListener;
pDSListener2->AddRef();
...
//Cleanup code. Check to see if the pointers are still active.
//If they are, call Release to release the interface.

if(pDSBPrimary != NULL)
{
  pDSBPrimary->Release();
  pDSBPrimary = NULL;
}

if(pDSListener != NULL)
{
  pDSListener->Release();
  pDSListener = NULL;
}

if(pDSListener2 != NULL)
{
  pDSListener2->Release();
  pDSListener2 = NULL;
}