もしあなたが nsCOMPtr
を前に使ったことがないのであれば、このセクションは、あなたにピッタリです。もしあなたがすでに nsCOMPtr
に慣れ親しんでいるのであれば、リファレンスマニュアル あるいは FAQ まで読み飛ばしたいかもしれません。心配しないでください。スタートガイドは短いです。
はじめに
nsCOMPtr
とは何ですか?
nsCOMPtr
はリークを防ぐのを助けるツールです。
nsCOMPtr
は「スマートポインタ」です。これは、文法的には通常の C や C++ の通常のポインタのように振舞うテンプレートクラスです。つまり、*
や ->
を使って、それが指すものを取り出すことができます。nsCOMPtr
は、XPCOM オブジェクトを指す生の C++ ポインタとは違い、AddRef
、Release
と QueryInterface
をあなたのために管理してくれるという点でスマートです。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 C、More Effective C です。
すべての XPCOM オブジェクトは、ヒープに割り当てられます。クライアントは、そのようなオブジェクトの実装について多くを知ることはできません。クライアントは「インタフェース」へのポインタを通じてのみ、それを参照します。すなわち、ポインタの静的な型は抽象基底クラスへのポインタであり、指されている実際のオブジェクトは、抽象基底クラスから派生しているクラスです。その XPCOM オブジェクトは、「インタフェースを実装する」と言われます。クライアントのオブジェクトへの参照は、一般的に「インタフェースポインタ」と呼ばれています。
オブジェクトは、たくさんのインタフェースを実装するかもしれません。それぞれのインタフェースについて、(少なくとも概念的には) 別々に、「参照のカウント」が行われます。つまり、インタフェースはそれを参照しているクライアントの数を保持しているということです。カウントが 0 になった時、インタフェースは自分自身を削除する
ことができます。クライアントには、この参照カウントの厳密さを保つことが求められています。そのため、インタフェースへの参照が獲得された時は参照カウントのインクリメントを行い、またそれを使わなくなった時は参照カウントのデクリメントを行わなければなりません。これを容易にするため、すべてのインタフェースは、メンバ関数 AddRef
、Release
を提供する抽象基底クラスから継承しています。
XPCOM の一つの規則は、インタフェースポインタを作成するか、返すかするどの関数もそれに対して、すでに AddRef
を実行していなければならないことです。呼び出し側は、参照をいつまでも保持することができ、いらなくなったら、Release
を呼びます。インタフェースへの最後のポインタに対して、Release
が呼ばれると、インタフェース (従って、通常は基となるオブジェクトも) は、自分自身を削除します。インタフェースに対する未解決の AddRef
がある限り、オブジェクトは存在し続けます。Release
を呼び忘れると、オブジェクトはリークし、すなわち、オブジェクトの記憶領域は決して取り戻されません。リークは、悪いことです。:-)
AddRef
と Release
の呼び出しを通じた参照を所有する参照と呼びます。それは、基となるオブジェクトに権利を持ちます。そのオブジェクトは、所有する参照がその権利を放棄するまで無くなりません。全ての参照が所有する参照である必要はありません。実際、もし二つのオブジェクトが何らかの形で (一時的にでも) お互いを所有しあうことになった場合、所有の輪を断ち切るなんらかの`例外的'メカニズムなしで、それらのオブジェクトを取り戻すのは、難しくなります。ドキュメント COM の所有のガイドライン は、所有権が必要になった時に、いくつかヒントを与えてくれます。以下のリストは、開始地点としていいですが、しかし決して完全ではありません。
所有する参照を使うのは、
- オブジェクトを生成した時。
- オブジェクトを生成した可能性のある関数からオブジェクトを受け取った場合。 例えば、
QueryInterface
やCreateInstance
のような、任意の「getter」関数。 望ましい getter はすべて、それらがつくり出したインタフェースポインタに対してAddRef
を実行し、所有する参照を提供します。 - その参照を、あなたがそれを取得した関数のスコープよりも長く保持する場合。 例えば、パラメタとして受け取り、それをメンバ変数として保持する場合。 [例えば、以下の 比較 1 を見てください]。
所有する参照を使わなくてもよいのは、
- オブジェクトがパラメタとして渡され、かつその関数のスコープよりも長くそれを保持する必要がない場合。
- 上手く定義されていることによって、当該オブジェクトの生存期間があなたのオブジェクトの生存期間を含んでいると分かっている場合。 例えば、ツリーのノードにおいて、 親のノードは、それらの子どもに対する所有する参照を保持しており、 子は、その親を所有する参照で保持する必要はありません。
これらにより、参照カウントをプログラマが手動で正しくするのは、大変であることが分かります。それは、簡単そうに見えますが、しかし実際には Release
を適切な時に実行するのは忘れやすいのです。あるいは、 AddRef
を多く呼びすぎたり、呼び出しが足りなかったりすることもあります。
nsCOMPtr
は、どのように役に立つのか?
nsCOMPtr
は、AddRef
、Release
、その他の煩わしい仕事をあなたのために管理します。nsCOMPtr
は、見掛けも振舞いも C が許している生の XPCOM インタフェースポインタのようです。しかし、nsCOMPtr
は、自分が指しているオブジェクトを所有していることを知っています。少し慣れる必要はありますが、しかし結果的に、タイピングが少なくて済み、きれいで、安全なコードを書くことができ、そしてリークが少なくなります。
例えば、ここに典型的な (とてもコンパクトな) コードの断片があります。これは、XPCOM インタフェースポインタをメンバ変数に代入しています。つまり、「setter」関数の本体です。生の XPCOM インタフェースポインタと nsCOMPtr
を並べて使用しています。
// 生の [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 インタフェースポインタを使うクラスは、mFooPtr
を Release
するためのデストラクタを必要とします。そして、mFooPtr
が NULL
(または何らかの正当な値) で初期化されることを保証するコンストラクタを必要とします。
nsCOMPtr
は、あなたが生の XPCOM インタフェースポインタを使うよりリークへの耐性があり、例外に対して安全で、だらだらとしないコードを書くのに役立ちます。nsCOMPtr
を使う時は、AddRef
、QueryInterface
を手動で呼ぶ必要はないでしょう。
それでもなお、 XPCOM を理解する必要があります。また、どの関数がAddRef
されたインタフェースポインタを返し、どの関数がそうでないものを返すのかを知っていなければなりません。また、あなたのプログラムロジックが循環参照によるゴミを作り出さないことを保障しなければなりません。nsCOMPtr
は、万能薬ではありません。しかしながら、それは、役に立ち、簡単に使え、よくテストされ、そして洗練されています。関数の作者があなたと協調することを必要としません。またあなたがそれを使うことにより、他人にそれを使うよう強制することもありません。
nsCOMPtr
を使う
基本
ほとんどの場合、あなたは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 ) // ... |
二つの主な違いがあります。最初の違い: あなたはもはや AddRef
や Release
を呼ぶ必要がありません。また呼んでもいけません。
// 生の [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
を注釈する必要があります。
// 生の [XP]COM インタフェースポインタ...
nsIFoo* foo;
GetFoo(&foo);
| // |nsCOMPtr|s...
nsCOMPtr<nsIFoo> foo;
GetFoo(getter_AddRefs(foo));
|
これで終りです。あなたは、もうnsCOMPtr
を使い始めるのに十分な知識を持っています。この他にnsCOMPtr
をもっと複雑な状況で使う時にあなたが知りたいであろう、いくつかの詳細な事柄があります。でもあなたが学んだことは、あなたが使う状況の 90% をカバーしています。
いくつかの詳細
あなたが nsCOMPtr
から最大限のことを引き出すのを手伝ういくつかの事があります。
しばしば、まず QueryInterface
を呼ぶことで、あなたはインタフェースポインタを得ます。 QueryInterface
は、他と同様に getter です。そして、上述したように getter_AddRefs
ルールを適用して、それを呼び出す一つの方法をすでに知っています。
// |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
には、それを呼び出すための特別に便利なものがあります。この便利なものは、タイプセーフで、これにより、nsCOMPtr
が QueryInterface
の結果から直接構築されます。正しい値からの構築は、構築後に代入するよりも効率的です。 この便利なものは、do_QueryInterface
指示子です。do_QueryInterface
を使うと、上記のサンプルはこのようになります。
// |nsCOMPtr| へ |QueryInterface| するベストな方法... nsresult rv; nsCOMPtr<nsIFoo> foo( do_QueryInterface(bar, &rv) ); // または、もし |nsResult| について気にしないのであれば nsCOMPtr<nsIFoo> foo( do_QueryInterface(bar) ); |
nsCOMPtr
は、嬉しいことに AddRef
と Release
を暗黙的に呼び出します。同じような方法は、QueryInterface
には、拡張されません。nsCOMPtr
は、代入において、do_QueryInterface
指示子を使った明示的な許可がなければ、QueryInterface
を実行しません。あなたは、もう隠れた問い合わせについて心配する必要はありません。しかしながら、もしあなたが問い合わせをするべきなのに、しなかった場合に注意してください。例えば、生のポインタを代入する場合で、C が代入を許可しているが XPCOM は許可していない場合。nsCOMPtr
は、実行時にアサートする でしょう。異なった型の XPCOM インタフェースに代入する時にはいつでも、do_QueryInterface
を使ってください。たとえ、その型がたまたま nsCOMPtr
の基底型から派生していてもです。
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
を実行したポインタを代入する時に役に立ちます。
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
は、自分のため (だけ) に、自動的に AddRef
と Release
を呼び出します。その他のすべての状況において、それは、生の 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
を使うべきです。つまり、あなたがそれに対して AddRef
と Release
を呼び出す所です。setter を単純にする場合、そしてコンストラクタ、デストラクタ、代入演算子を除去する場合、nsCOMPtr
をメンバ変数として使うべきです。QueryInterface
の呼び出しをおおむね快適にし、エラー処理を避けるための複雑なロジックを除去する場合、nsCOMPtr
をスタック上で使うべきです。
どこでnsCOMPtr
を使うべきではないですか?
所有する参照を必要としないところでは、nsCOMPtr
を使わないでください。COM の所有のガイドライン を見てください。nsCOMPtr
は XPCOM インタフェースとともに使われるように設計されています。そのため、以下 に示すように特定の例外を伴うインタフェースでないものと一緒には使わないでください。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>& )
nsCOMPtr
をconst
参照渡ししない
上の書き方と全く同じで、この方法は役に立たないどころか、実害があります。もし呼び出し側が生ポインタを渡した場合には、nsCOMPtr
を値渡しするのと同じ良く無いことが起こります。
void f( nsCOMPtr<T>* )
できれば nsCOMPtr
のアドレス渡しは避ける
この方法は、呼び出し側に、それが nsCOMPtr
を使用することと、ちょっとした余分な仕事を要求します。と言うのは、nsCOMPtr
の operator&
は (キャストによるリーク を防ぐために: バグ 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 で示すようなコンパイラの問題にも、注意しなければいけないですが]。あなたは、それを通じて明示的に AddRef
や Release
を呼ばなくてよいです。また、コンパイラもそれを許しません。あなたが nsCOMPtr
を変更しなければ使うところのできない唯一の場所は、生の XPCOM インタフェースポインタが`出力'引数である場所です。この場合、あなたは nsCOMPtr
を getter_AddRefs
でラップします [比較 4を見てください]。
nsCOMPtr
に代入した時に、(生の XPCOM インタフェースポインタであっても nsCOMPtr
であっても、) 通常は、追加の指示子なしに単にもう一つのポインタを渡すだけです [例えば、比較 1 の nsCOMPtr
の方を見てください]。上述したように、指示子なしに、nsCOMPtr
は、もし古い対象があれば、それに対して、Release
を呼び出し、そして新しい方に対して、AddRef
を呼び出します。このようにするのが適切なのは、新しい参照に対して責任をとるために、あなたが代入したものに対してまだ AddRef
を実行していない時です。これは、あなたが取得する関数を呼び出したのではないポインタを代入する時によくある場合です。例えば、引き数として渡されたものや、構造体から抜きだしたものなどです。
あなたは、nsCOMPtr
に、新しい値を dont_AddRef
でラップすることにより、代入において新しい値を AddRef
する必要がないことを伝えることができます。例えば、すべての望ましい XPCOM getter のように、あなたのためにすでに AddRef
を呼び出している関数から新しい値を得た場合に、これを行ってください。
あなたは、ポインタを異なったインタフェース型に代入してはいけません。あなたは、まず正しい型に問い合わせる必要があります [例えば、比較 6 と周辺の議論を見てください]。nsCOMPtr
は、決して QueryInterface
を暗黙的に呼び出しません。つまり、あなたは自分でそれを呼ばなければいけません。あるいは、明示的に do_QueryInterface
を使って、nsCOMPtr
にそれを呼ぶように依頼しなければいけません。do_QueryInterface
指示子は、あなたが代入の一部として問い合わせをするのを許します。このよりよい便利な機構により、構築してから正しい値を後で代入するのではなく、(代入での) 右の値から nsCOMPtr
を直接構築されます。構築に続いて代入するより、構築だけで済ませる方が効率的です。合理的である限り、代入と同時に構築する方を選んでください。AddRef
したポインタを返す関数に対して、do_QueryInterface
を適用しないように注意してください。[説明のために この短いセクション を見てください。]
より詳しいことについては、リファレンスマニュアル に続きます。