Getting Started Guide

 

もしあなたが nsCOMPtr を前に使ったことがないのであれば、このセクションは、あなたにピッタリです。もしあなたがすでに nsCOMPtr に慣れ親しんでいるのであれば、リファレンスマニュアル あるいは FAQ まで読み飛ばしたいかもしれません。心配しないでください。スタートガイドは短いです。

はじめに

nsCOMPtrとは何ですか?

nsCOMPtrはリークを防ぐのを助けるツールです。

nsCOMPtr は「スマートポインタ」です。これは、文法的には通常の C や C++ の通常のポインタのように振舞うテンプレートクラスです。つまり、*-> を使って、それが指すものを取り出すことができます。nsCOMPtr は、XPCOM オブジェクトを指す生の C++ ポインタとは違い、AddRefReleaseQueryInterface をあなたのために管理してくれるという点でスマートです。nsCOMPtr は、以下のソースファイルで定義されています。

...ただ、まだそこを見たくはないは思わないでしょうけど。

nsCOMPtr を使うことで、生の XPCOM インタフェースポインタを使うよりも、短く、きれいで、明確で、安全なコードを書くことができます。

[XP]COM の基本: 所有権と参照カウント

これは、 XPCOM の基本的な事項に関する軽い記事です。あなたはすでに(XPCOM について)知っている必要がありますし、この短いセクションにざっと目を通せるようにすべきです。もしあまり馴染みのない事が書いてあるのであれば、あなたはまだ nsCOMPtr を読む準備ができていません。COM の背後の基本的ルールと論証については、以下の情報が有用です。Essential COM (Don Box 著)。Don Box は、COM について、より詳細なこと、わな、落とし穴について、Effective COM で述べています。あなたは C++ についての適切な知識も持っているべきです。おそらくこのトピックについてとても助けになる 3 冊の本は、Bjarne Stroustrup の The C Programming Language、Scott Meyers の Effective CMore Effective C です。

すべての XPCOM オブジェクトは、ヒープに割り当てられます。クライアントは、そのようなオブジェクトの実装について多くを知ることはできません。クライアントは「インタフェース」へのポインタを通じてのみ、それを参照します。すなわち、ポインタの静的な型は抽象基底クラスへのポインタであり、指されている実際のオブジェクトは、抽象基底クラスから派生しているクラスです。その XPCOM オブジェクトは、「インタフェースを実装する」と言われます。クライアントのオブジェクトへの参照は、一般的に「インタフェースポインタ」と呼ばれています。

オブジェクトは、たくさんのインタフェースを実装するかもしれません。それぞれのインタフェースについて、(少なくとも概念的には) 別々に、「参照のカウント」が行われます。つまり、インタフェースはそれを参照しているクライアントの数を保持しているということです。カウントが 0 になった時、インタフェースは自分自身を削除することができます。クライアントには、この参照カウントの厳密さを保つことが求められています。そのため、インタフェースへの参照が獲得された時は参照カウントのインクリメントを行い、またそれを使わなくなった時は参照カウントのデクリメントを行わなければなりません。これを容易にするため、すべてのインタフェースは、メンバ関数 AddRefRelease を提供する抽象基底クラスから継承しています。

XPCOM の一つの規則は、インタフェースポインタを作成するか、返すかするどの関数もそれに対して、すでに AddRef を実行していなければならないことです。呼び出し側は、参照をいつまでも保持することができ、いらなくなったら、Release を呼びます。インタフェースへの最後のポインタに対して、Release が呼ばれると、インタフェース (従って、通常は基となるオブジェクトも) は、自分自身を削除します。インタフェースに対する未解決の AddRef がある限り、オブジェクトは存在し続けます。Release を呼び忘れると、オブジェクトはリークし、すなわち、オブジェクトの記憶領域は決して取り戻されません。リークは、悪いことです。:-)

AddRefRelease の呼び出しを通じた参照を所有する参照と呼びます。それは、基となるオブジェクトに権利を持ちます。そのオブジェクトは、所有する参照がその権利を放棄するまで無くなりません。全ての参照が所有する参照である必要はありません。実際、もし二つのオブジェクトが何らかの形で (一時的にでも) お互いを所有しあうことになった場合、所有の輪を断ち切るなんらかの`例外的'メカニズムなしで、それらのオブジェクトを取り戻すのは、難しくなります。ドキュメント COM の所有のガイドライン は、所有権が必要になった時に、いくつかヒントを与えてくれます。以下のリストは、開始地点としていいですが、しかし決して完全ではありません。

所有する参照を使うのは、

  • オブジェクトを生成した時。
  • オブジェクトを生成した可能性のある関数からオブジェクトを受け取った場合。 例えば、QueryInterfaceCreateInstance のような、任意の「getter」関数。 望ましい getter はすべて、それらがつくり出したインタフェースポインタに対して AddRef を実行し、所有する参照を提供します。
  • その参照を、あなたがそれを取得した関数のスコープよりも長く保持する場合。 例えば、パラメタとして受け取り、それをメンバ変数として保持する場合。 [例えば、以下の 比較 1 を見てください]。

所有する参照を使わなくてもよいのは、

  • オブジェクトがパラメタとして渡され、かつその関数のスコープよりも長くそれを保持する必要がない場合。
  • 上手く定義されていることによって、当該オブジェクトの生存期間があなたのオブジェクトの生存期間を含んでいると分かっている場合。 例えば、ツリーのノードにおいて、 親のノードは、それらの子どもに対する所有する参照を保持しており、 子は、その親を所有する参照で保持する必要はありません。

これらにより、参照カウントをプログラマが手動で正しくするのは、大変であることが分かります。それは、簡単そうに見えますが、しかし実際には Release を適切な時に実行するのは忘れやすいのです。あるいは、 AddRef を多く呼びすぎたり、呼び出しが足りなかったりすることもあります。

nsCOMPtr は、どのように役に立つのか?

nsCOMPtr は、AddRefRelease、その他の煩わしい仕事をあなたのために管理します。nsCOMPtrは、見掛けも振舞いも C が許している生の XPCOM インタフェースポインタのようです。しかし、nsCOMPtr は、自分が指しているオブジェクトを所有していることを知っています。少し慣れる必要はありますが、しかし結果的に、タイピングが少なくて済み、きれいで、安全なコードを書くことができ、そしてリークが少なくなります。

例えば、ここに典型的な (とてもコンパクトな) コードの断片があります。これは、XPCOM インタフェースポインタをメンバ変数に代入しています。つまり、「setter」関数の本体です。生の XPCOM インタフェースポインタと nsCOMPtr を並べて使用しています。

 

比較 1. メンバ変数を設定する
// 生の [XP]COM インタフェースポインタ...
// 仮定: |nsIFoo* mFooPtr;|

/*    もし |NULL| でなく新しい値なら、|AddRef| し    それを代入します。もし古い値があれば、    |Release| します (そうやってリークを防ぎます)。    この割り当て順序は特別で、特定の所有者バグを防ぐために   使われなくてはなりません。  */

NS_IF_ADDREF(aFooPtr); nsIFoo* temp = mFooPtr;
mFooPtr = aFooPtr;
NS_IF_RELEASE(temp);
// |nsCOMPtr|...
// 仮定: |nsCOMPtr<nsIFoo> mFooPtr;|

/*    この代入は、|mFooPtr| に古い値が    あれば自動的にそれを |Release| し、    新しい値に対して、先ほど触れた所有者バグを    防ぐために適切な順序で |AddRef| を    呼び出します。  */





mFooPtr = aFooPtr;

付け加えると、生の XPCOM インタフェースポインタを使うクラスは、mFooPtrRelease するためのデストラクタを必要とします。そして、mFooPtrNULL (または何らかの正当な値) で初期化されることを保証するコンストラクタを必要とします。

nsCOMPtrは、あなたが生の XPCOM インタフェースポインタを使うよりリークへの耐性があり、例外に対して安全で、だらだらとしないコードを書くのに役立ちます。nsCOMPtr を使う時は、AddRefQueryInterface を手動で呼ぶ必要はないでしょう。

それでもなお、 XPCOM を理解する必要があります。また、どの関数がAddRefされたインタフェースポインタを返し、どの関数がそうでないものを返すのかを知っていなければなりません。また、あなたのプログラムロジックが循環参照によるゴミを作り出さないことを保障しなければなりません。nsCOMPtr は、万能薬ではありません。しかしながら、それは、役に立ち、簡単に使え、よくテストされ、そして洗練されています。関数の作者があなたと協調することを必要としません。またあなたがそれを使うことにより、他人にそれを使うよう強制することもありません。

nsCOMPtr を使う

基本

ほとんどの場合、あなたはnsCOMPtrを生の XPCOM インタフェースポインタと全く同じように使うでしょう。宣言時のわずかな違いに注意してください。

 

比較 2. 類似性: nsCOMPtrは、文法的に生の XPCOM インタフェースポインタに似ている。
// 生の [XP]COM インタフェースポインタ...

nsIFoo* fooPtr = 0;  // ...
fooPtr->SomeFunction(x, y, z);
AnotherFunction(fooPtr);

if ( fooPtr )
  // ...

if ( fooPtr == foo2Ptr )
  // ...
// |nsCOMPtr|...

nsCOMPtr<nsIFoo> fooPtr;
// ...
fooPtr->SomeFunction(x, y, z);
AnotherFunction(fooPtr);

if ( fooPtr )
  // ...

if ( fooPtr == foo2Ptr )
  // ...

二つの主な違いがあります。最初の違い: あなたはもはや AddRefRelease を呼ぶ必要がありません。また呼んでもいけません。

 

Comparison 3. Differences: AddRef and Release are illegal for nsCOMPtrs.
// 生の [XP] COMインタフェースポインタ...
// 仮定: |nsIFoo* mFooPtr;|

  /*     注意: この順序はどっちみち生のポインタが     代入された正しい順序ではありません     (比較 1 を参照してください) しかし、     ここでは、この比較が必要です。   */

NS_IF_RELEASE(mFooPtr);

mFooPtr = aFooPtr;
NS_IF_ADDREF(mFooPtr);

// |nsCOMPtr|...
// 仮定: |nsCOMPtr<nsIFoo> mFooPtr;|

  /*     もはや |AddRef| や |Release| を呼ぶ     必要もありませんし、コンパイラはそれを     エラーにします。   */



NS_IF_RELEASE(mFooPtr);
  // エラー: |Release| はプライベートです。
mFooPtr = aFooPtr;
NS_IF_ADDREF(mFooPtr);
  // エラー: |AddRef| はプライベートです。

二番目の違い: あなたは、生の XPCOM インタフェースポインタのパラメタを通じて、結果を返すことを期待して、nsCOMPtr のアドレスを getter に渡すことができません。あなたは、getter_AddRefs 指示子で、nsCOMPtr を注釈する必要があります。

 

比較 4.違い: nsCOMPtrを`出力パラメタ'として使う時に、getter_AddRefsを適用する。
// 生の [XP]COM インタフェースポインタ...

nsIFoo* foo;

GetFoo(&foo);
// |nsCOMPtr|s...

nsCOMPtr<nsIFoo> foo;

GetFoo(getter_AddRefs(foo));

これで終りです。あなたは、もうnsCOMPtrを使い始めるのに十分な知識を持っています。この他にnsCOMPtrをもっと複雑な状況で使う時にあなたが知りたいであろう、いくつかの詳細な事柄があります。でもあなたが学んだことは、あなたが使う状況の 90% をカバーしています。

いくつかの詳細

あなたが nsCOMPtr から最大限のことを引き出すのを手伝ういくつかの事があります。

しばしば、まず QueryInterface を呼ぶことで、あなたはインタフェースポインタを得ます。 QueryInterface は、他と同様に getter です。そして、上述したように getter_AddRefs ルールを適用して、それを呼び出す一つの方法をすでに知っています。

nsCOMPtrQueryInterface する難しい方法
// |nsCOMPtr| に |QuertyInterface| するやり方 (最良のやり方ではないですが)...

nsCOMPtr<nsIFoo> foo;

nsresult rv = bar->QueryInterface(NS_GET_IID(nsIFoo), getter_AddRefs(foo));

  // または、あなたが [XP]COM をよく知っているプログラマ
  // ならば、タイプセーフ版を使ってください...
nsresult rv = CallQueryInterface(bar, getter_AddRefs(foo));

QueryInterface はしばしば使われるので、nsCOMPtr には、それを呼び出すための特別に便利なものがあります。この便利なものは、タイプセーフで、これにより、nsCOMPtrQueryInterface の結果から直接構築されます。正しい値からの構築は、構築後に代入するよりも効率的です。 この便利なものは、do_QueryInterface 指示子です。do_QueryInterface を使うと、上記のサンプルはこのようになります。

nsCOMPtr への QueryInterface の仕方
// |nsCOMPtr| へ |QueryInterface| するベストな方法...

nsresult rv;
nsCOMPtr<nsIFoo> foo( do_QueryInterface(bar, &rv) );

  // または、もし |nsResult| について気にしないのであれば
nsCOMPtr<nsIFoo> foo( do_QueryInterface(bar) );

nsCOMPtr は、嬉しいことに AddRefRelease を暗黙的に呼び出します。同じような方法は、QueryInterface には、拡張されませんnsCOMPtr は、代入において、do_QueryInterface 指示子を使った明示的な許可がなければ、QueryInterface を実行しません。あなたは、もう隠れた問い合わせについて心配する必要はありません。しかしながら、もしあなたが問い合わせをするべきなのに、しなかった場合に注意してください。例えば、生のポインタを代入する場合で、C が代入を許可しているが XPCOM は許可していない場合。nsCOMPtr は、実行時にアサートする でしょう。異なった型の XPCOM インタフェースに代入する時にはいつでも、do_QueryInterface を使ってください。たとえ、その型がたまたま nsCOMPtr の基底型から派生していてもです。

 

比較 6. do_QueryInterfaceXPCOM の型のエラーを防ぐ。
class nsIBar
  : public nsIFoo ... { ... };

nsIBar* p = ...;

  // C は、すべての |nsIBar*| が
  // |nsIFoo*| であるとみなします。そのため、C は
  // これを許可します...
nsCOMPtr<nsIFoo> foo = p;
  //  ...たとえそれが [XP]COM の型の
  //  エラーだとしてもそうです。
class nsIBar
  : public nsIFoo ... { ... };

nsIBar* p = ...;



  // ここでは、型のエラーはありません...
nsCOMPtr<nsIFoo> foo( do_QueryInterface(p) );


覚えておいてください。C の型のシステムと XPCOM の型のシステムは、互いに独立しているものです。XPCOM インタフェースは、C の抽象基底クラスとして表現されているため、C に違いを処理させたり、あるいはインタフェースの型の間を取り持つために C のキャストを使ったりしたくなるかもしれません。これは、間違いです。XPCOM の型の間で許されているのは、QueryInterface を使うことだけです。上記の例では、C が p から引き出す nsIFoo*p->QueryInterface() が返すものと同一のものであると仮定する理由はありません。

dont_AddRef は、同じような指示子で、例えば、その関数の結果としてポインタを返す getter を呼んだなどの理由で、すでに AddRef を実行したポインタを代入する時に役に立ちます。

dont_AddRef の使用
nsCOMPtr<nsIFoo> foo( dont_AddRef(CreateFoo()) );
  // |CreateFoo| は、すべての望ましい getter が行うように、その結果を |AddRef| します。 

nsCOMPtrしないこと

nsCOMPtr は、所有する参照として振舞うために必要なすべてのことを行います。しかしながら、与えられた nsCOMPtr は、他の所有ポインタを作ることには協力しません。どうやって nsCOMPtr代入される時に自動的にポインタを AddRef するかを学習した後、それが参照される時にも同じことをすると仮定するのは、自然です。この誤解を示すコード断片を載せます。

// |nsCOMPtr| に関する間違った仮定...

nsresult
nsCacheRecord::GetFileSpec( nsIFileSpec** aFileSpecResult )
    /*       ...呼び出し側の |nsFileSpec*| (呼び出し側がアドレスを設定します) に       私のメンバ変数の |mFileSpec| (|nsCOMPtr型|) のコピーが代入されます。       つまり、この関数は「getter」です。        覚えてください: 望ましい [XP]COM getter は、いつも結果に対して |AddRef| を実行します。     */
  {
    // ...
    *aFileSpec = mFileSpec;
      // |nsCOMPtr| は、参照カウントに気をつけるべきです。いいですか?
    return NS_OK;
  }

明らかに、作者は (いくつかの疑問を持ちながらかもしれませんが)、nsCOMPtr つまり mFileSpec は、*aFileSpec への代入される時、自動的に AddRef を呼ぶと信じています。この場合は違いますnsCOMPtr は、自分のため (だけ) に、自動的に AddRefRelease を呼び出します。その他のすべての状況において、それは、生の XPCOM ポインタを置き換えるスロットとして設計されています。nsCOMPtr が生のポインタが必要とされているところで使われていたら、nsCOMPtr は自動的にそれを提供します。

// |nsCOMPtr| は、生のポインタが必要とされている場合は、それを提供します...

nsCOMPtr<nsIFoo> foo = ...;

  // 1.  生のポインタに代入
nsIFoo* raw_foo = foo;

  // 2.  別の |nsCOMPtr| に代入
nsCOMPtr<nsIFoo> foo2 = foo;

  // 3.  パラメタとして
SetFoo(foo);

  // 4.  |if| 式の中で値をテスト
  // 5.  メンバ関数の呼び出し
if ( foo )
  foo->DoSomething();

これらすべての場合において、かなり正確に同じコードが実行されます (2 番目のケースは、少し違いますが、意図は同じです)。それぞれの場合において、あなたは本質的に自分の目的のために生のポインタの値を取り出しています。もし nsCOMPtr が値に対して、その都度 AddRef を実行すると、4 番目のケースと 5 番目のケースではあきらかにいつもリークを作り出してしまいます。ケース 3 の SetFoo は、場合によって、二つの異なった書き方で書かれる必要があります。それは、nsCOMPtr が与えられた場合は、値に対してすでに AddRef が実行されていることが分かり、そして生のポインタが与えられた場合、値に対して AddRef は実行されていないことがわかるためです。実際、矛盾はこれらよりもっと深くまで広がります。これらすべてのケースは、「出力」に対して自動的に AddRef を実行すると、nsCOMPtr と生のポインタがクライアントの視点から見て異なる振舞いをすることになるということを示しています。同じように振舞うようにさせるのが目的であり、そのため nsCOMPtr は、置き換えのスロットになりうるのです(自分の「所有権」について管理することを守らせることにより)。

あなたが今知ったことから、ルールは明らかです。上述したように、そうしないように指示しない限り、nsCOMPtr は、代入される時に AddRef を実行します。nsCOMPtr は、参照される時は何もしません。

どこでnsCOMPtrを使うべきでしょうか?

インタフェースポインタを所有する参照として使うところでは、どこでも nsCOMPtr を使うべきです。つまり、あなたがそれに対して AddRefRelease を呼び出す所です。setter を単純にする場合、そしてコンストラクタ、デストラクタ、代入演算子を除去する場合、nsCOMPtr をメンバ変数として使うべきです。QueryInterface の呼び出しをおおむね快適にし、エラー処理を避けるための複雑なロジックを除去する場合、nsCOMPtr をスタック上で使うべきです。

どこでnsCOMPtrを使うべきではないですか?

所有する参照を必要としないところでは、nsCOMPtrを使わないでください。COM の所有のガイドライン を見てください。nsCOMPtrXPCOM インタフェースとともに使われるように設計されています。そのため、以下 に示すように特定の例外を伴うインタフェースでないものと一緒には使わないでください。XPCOM の中で nsCOMPtr を使わないでください。それらをプレーンな古い C コード上で使わないでください。もちろん、nsCOMPtrは C だけの構築物です。nsCOMPtr を決して キャストしないで ください。それをすると、ほとんどリークが保証されたようなものです。

インタフェースでないクラスのための nsCOMPtr

適切にフォーマットした解答を追加する予定です。当面の間、詳細全体は この news 投稿 で利用可能です。

関数識別子内の nsCOMPtr

一般的に、XPCOM (つまり、「スクリプタブル」) 関数の識別子内で、nsCOMPtr を使いたいとは思わないでしょう。nsCOMPtr は現在 IDL により直接サポートはされていません。しかし、あなたは時々スクリプタブルでない関数内で nsCOMPtr を使いたくなるかもしれません。

nsCOMPtr<T> f() nsCOMPtr をリターン値として返さない

この方法は危険です。AddRef されたポインタを関数のリターン値として返すことは、ほとんどどの様な形で行なっても、リークや無効なポインタなどの、かなりひどい潜在的エラーに行きつきます。 nsCOMPtr をリターンすることは (クライアントがそれに所有権を与えたことをクライアントに教えるので) よい考えのように見えますが、これは無効なポインタを引き起こします。以下のコードを考えてみてください。

// |nsCOMPtr|を返してはいけません...
nsCOMPtr<nsIFoo> CreateFoo();
// ...

nsIFoo* myFoo = CreateFoo(); // おっと: |myFoo| はもう無効!
  // |CreateFoo| は |nsCOMPtr| を返すけれど、
  // |nsCOMPtr| はこの代入のあと正しく自動的に |Release| する
  // 今 |myFoo| は削除されたオブジェクト
  // を参照している。

already_AddRefed<T> をリターンすることにより、呼び出し側に、この危険なしにそれらに所有権を与えたことを通知できます (バグ 59212参照)。nsCOMPtr は、already_AddRefed された値は、AddRef すべきではない事を知るようになります。

// 好ましい方法: もし、ポインタを返す必要があるなら、|already_AddRefed| を使うこと...
already_AddRefed<nsIFoo> CreateFoo();
// ...

nsIFoo* myFoo1 = CreateFoo(); // 無効にならない
nsCOMPtr<nsIFoo> myFoo2( CreateFoo() ); // リークしない
nsCOMPtr<nsIFoo> myFoo3( dont_AddRef(CreateFoo()) ); // 冗長だが認められており正しい

これを、既に AddRef した生ポインタをリターンすることを原因とする、最も頻繁に起こりうるリークと比べてみてください。

// 生のポインタを返さないでください、リークを誘発します...
nsIFoo* CreateFoo(); // |AddRef| されたポインタを返してください
// ...

nsCOMPtr<nsIFoo> myFoo = CreateFoo(); // おっと: リークだ
nsCOMPtr<nsIFoo> myFoo( dont_AddRef(CreateFoo()) );
  // |CreateFoo| その結果を既に |AddRef| しているため、私たちは |nsCOMPtr|
  // をそうしないように覚えておかなくてはなりません。それは忘れやすいことです。
  // 関数の戻り値としてポインタを返さないか、さもなければ上記のように
  // |already_AddRefed<T>| を返すかしてあらかじめ防いでください。

 

void f( nsCOMPtr<T> ) nsCOMPtrを値渡ししない

この方法は役に立たないどころか、実害があります。引き数は関数コールと同じ生存期間を保証されるので、引き数を AddRef する必要はありません。関数コールを超えて生き残る構造体のメンバに値を格納する時のみ、AddRef が必要になります。これは、関数の引き数ではなく、構造体の適切なメンバが nsCOMPtr であるべきことを意味します。更にこの書き方は、呼び出し側に、単に関数をコールするために nsCOMPtr が必要なのではないかと思わせ、混乱させます。

void f( const nsCOMPtr<T>& ) nsCOMPtrconst 参照渡ししない

上の書き方と全く同じで、この方法は役に立たないどころか、実害があります。もし呼び出し側が生ポインタを渡した場合には、nsCOMPtr を値渡しするのと同じ良く無いことが起こります。

void f( nsCOMPtr<T>* ) できれば nsCOMPtr のアドレス渡しは避ける

この方法は、呼び出し側に、それが nsCOMPtr を使用することと、ちょっとした余分な仕事を要求します。と言うのは、nsCOMPtroperator& は (キャストによるリーク を防ぐために: バグ 59414 参照) private だからです。この方法は、「入出力」引き数として宣言する事により、以下のように可能ですが、nsCOMPtr を参照渡しする方が好ましいでしょう。

// |nsCOMPtr| のポインタ渡しは余計な仕事を増やすk...
void f( nsCOMPtr<nsIFoo>* );
// ...

nsCOMPtr<nsIFoo> myFoo = ...;

f( address_of(myFoo) );
void f( nsCOMPtr<T>& ) ちゃんとnsCOMPtrを「入出力」引き数として参照渡しする

これは「入出力」引き数を提供するために好ましい方法です。もし代りに生ポインタを使った場合、関数内部では、入力値として呼び出し側がどの所有する関係を持っているかが、分らなくなります。結果として、新しい値を代入する前に Release すべきかどうかが分らなくなります。引き数を nsCOMPtr&、として宣言する事により、関係が明確になります。

要約

nsCOMPtr は、所有する参照です。それが指すものはなんであれ AddRef され、nsCOMPtr をその「所有者」の一つとしてカウントします。nsCOMPtr は、nsCOMPtr が違うオブジェクトを指すために解放されるか、nsCOMPtr がスコープを抜けようとしているためかいずれにしろ、解放される前に必ず Release を呼び出します。新しい値が nsCOMPtr に割り当てられる時は、nsCOMPtr は、いつも自動的に、もし古い参照があれば、それを Release し、(そしてあなたがすでに実行済であると明示しなければ) 新しい方を AddRef します。

あなたはnsCOMPtrを厳密にほとんどすべての場合で生の XPCOM インタフェースポインタとして使うことができます [|比較 5 で示すようなコンパイラの問題にも、注意しなければいけないですが]。あなたは、それを通じて明示的に AddRefRelease を呼ばなくてよいです。また、コンパイラもそれを許しません。あなたが nsCOMPtr を変更しなければ使うところのできない唯一の場所は、生の XPCOM インタフェースポインタが`出力'引数である場所です。この場合、あなたは nsCOMPtrgetter_AddRefs でラップします [比較 4を見てください]。

nsCOMPtr に代入した時に、(生の XPCOM インタフェースポインタであっても nsCOMPtr であっても、) 通常は、追加の指示子なしに単にもう一つのポインタを渡すだけです [例えば、比較 1nsCOMPtr の方を見てください]。上述したように、指示子なしに、nsCOMPtr は、もし古い対象があれば、それに対して、Release を呼び出し、そして新しい方に対して、AddRef を呼び出します。このようにするのが適切なのは、新しい参照に対して責任をとるために、あなたが代入したものに対してまだ AddRef を実行していない時です。これは、あなたが取得する関数を呼び出したのではないポインタを代入する時によくある場合です。例えば、引き数として渡されたものや、構造体から抜きだしたものなどです。

あなたは、nsCOMPtr に、新しい値を dont_AddRef でラップすることにより、代入において新しい値を AddRef する必要がないことを伝えることができます。例えば、すべての望ましい XPCOM getter のように、あなたのためにすでに AddRef を呼び出している関数から新しい値を得た場合に、これを行ってください。

あなたは、ポインタを異なったインタフェース型に代入してはいけません。あなたは、まず正しい型に問い合わせる必要があります [例えば、比較 6 と周辺の議論を見てください]。nsCOMPtr は、決して QueryInterface を暗黙的に呼び出しません。つまり、あなたは自分でそれを呼ばなければいけません。あるいは、明示的に do_QueryInterface を使って、nsCOMPtr にそれを呼ぶように依頼しなければいけません。do_QueryInterface 指示子は、あなたが代入の一部として問い合わせをするのを許します。このよりよい便利な機構により、構築してから正しい値を後で代入するのではなく、(代入での) 右の値から nsCOMPtr を直接構築されます。構築に続いて代入するより、構築だけで済ませる方が効率的です。合理的である限り、代入と同時に構築する方を選んでください。AddRef したポインタを返す関数に対して、do_QueryInterface を適用しないように注意してください。[説明のために この短いセクション を見てください。]

より詳しいことについては、リファレンスマニュアル に続きます。

 

ドキュメントのタグと貢献者

タグ: 
最終更新者: fscholz,