次の方法で共有


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

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

オブジェクトが作成されると、システムは必要なメモリ リソースを割り当てる。必要がなくなったオブジェクトは、破棄する必要がある。システムは、そのメモリを他の目的に使える。C++ オブジェクトでは、new および delete の各演算子を使ってオブジェクトの有効期間を直接制御できる。コンポーネント オブジェクト モデル (COM) では、オブジェクトを直接作成または破棄できない。これは、同じオブジェクトを複数のアプリケーションが使っている可能性があるからである。このようなオブジェクトを 1 つのアプリケーションが破棄すると、他のアプリケーションでは高い確率でエラーが発生する。COM では、参照カウントというシステムを使ってオブジェクトの有効期間を制御している。

オブジェクトの参照カウントは、そのオブジェクトのいずれかのインターフェイスが要求された回数を示す。インターフェイスが要求されるたびに、参照カウントはインクリメントされる。必要のなくなったインターフェイスをアプリケーションが解放すると、参照カウントはデクリメントされる。オブジェクトは、参照カウントが 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;
}