From a065e04d529da1d847b5062a12c46d916408bf32 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Tue, 8 Dec 2020 21:46:22 -0500 Subject: update based on https://github.com/mdn/yari/issues/2028 --- .../frequently_asked_questions/index.html | 91 ---- .../getting_started_guide/index.html | 294 ------------ .../mozilla/tech/xpcom/using_nscomptr/index.html | 24 - .../using_nscomptr/reference_manual/index.html | 523 --------------------- .../status,_recent_changes,_and_plans/index.html | 36 -- 5 files changed, 968 deletions(-) delete mode 100644 files/ja/mozilla/tech/xpcom/using_nscomptr/frequently_asked_questions/index.html delete mode 100644 files/ja/mozilla/tech/xpcom/using_nscomptr/getting_started_guide/index.html delete mode 100644 files/ja/mozilla/tech/xpcom/using_nscomptr/index.html delete mode 100644 files/ja/mozilla/tech/xpcom/using_nscomptr/reference_manual/index.html delete mode 100644 files/ja/mozilla/tech/xpcom/using_nscomptr/status,_recent_changes,_and_plans/index.html (limited to 'files/ja/mozilla/tech/xpcom/using_nscomptr') diff --git a/files/ja/mozilla/tech/xpcom/using_nscomptr/frequently_asked_questions/index.html b/files/ja/mozilla/tech/xpcom/using_nscomptr/frequently_asked_questions/index.html deleted file mode 100644 index 1edb362717..0000000000 --- a/files/ja/mozilla/tech/xpcom/using_nscomptr/frequently_asked_questions/index.html +++ /dev/null @@ -1,91 +0,0 @@ ---- -title: Frequently Asked Questions -slug: Mozilla/Tech/XPCOM/Using_nsCOMPtr/Frequently_Asked_Questions -tags: - - XPCOM -translation_of: Mozilla/Tech/XPCOM/Using_nsCOMPtr/Frequently_Asked_Questions ---- -

-

このセクションは、もしあなたが壊れたビルドを直したり、即答できる明らかな疑問を持っていたり、あるいは リファレンスマニュアル を読む時間がない場合に役に立つでしょう。この FAQ は、通常は適切な答えへ直接的に参照するだけです。もしあなたが単に nsCOMPtr について学ぶことを期待しているのであれば、スタートガイド にもっと良い紹介があります。 -

FAQ は、探しているものを早く見つけやすくするために複数のセクションに分かれています。 ほとんどの場合、答えは上記のリファレンスマニュアルを参照しているだけです。 同じことを 2 度説明する必要はありません :-) -

-

ビルド時のエラー

-

ビルドが壊れました。エラーがあなた自身のコードでもなく、あなたのプラットフォームのコードでもなく、nsCOMPtrの 中で起こっており、そしてあなたはそれを疑っています。あなたは正しい場所を見ています。 -

-

nsCOMPtr と生の [XP]COM インタフェースポインタを比較しています

-

nsCOMPtr を後に宣言しているクラスに対して宣言しています

-

XPCOM とリンクしていません

-

nsCOMPtr.h をインクルードしていません

-

NSCAP_FEATURE_DEBUG_PTR_TYPES の設定が違います

-

実行時のエラー

-

NS_ASSERTION "QueryInterface needed"

-

NS_PRECONDITION "You can't dereference a NULL nsCOMPtr with operator->()"

-

NS_PRECONDITION "You can't dereference a NULL nsCOMPtr with operator*()"

-

どうやって...

-

nsCOMPtr を初期化するのですか?

-

nsCOMPtr がスコープを抜ける前にそれを Release するのですか?

-

それに 0 を代入してください。nsCOMPtr が新しい値を取る時は、古い値があれば、いつもそれを Release します。値 0 を割り当てるということは、NULL の生のポインタを割り当てるのと同じようなことです。古い対象は、Release されます。(より詳しくは、初期化と代入 を見てください。) -

ただし、これには小さなパフォーマンス上のペナルティがあることに注意してください。nsCOMPtr は、デストラクタの処理の中で、その時持つ値を Release しようとします。最良の解決法は、nsCOMPtr の生存期間をその参照を保持したい期間だけに調整することです。例えば、この例ではブロックを使っています。 -

- - -
-
// 最も効率的な枠組は、|nsCOMPtr| がちょうどその参照を
-// 保持する必要のある期間だけのスコープを設定することです。
-
-nsresult
-SomeLongFunction( nsIBar* aBar )
-  {
-    nsresult rv;
-    // ...
-
-    {
-      // |nsIFoo| インタフェースは、短期間だけ必要です。
-      // そのため、その生存期間をブロック文の中で
-      // 宣言して、制御します。
-
-      nsCOMPtr<nsIFoo> foo( do_QueryInterface(aBar, &rv) );
-      if ( foo )
-        foo->DoSomeFooThing();
-
-      // |foo| がスコープをはずれました。そのため、ここで |Release| が実行されます。
-    }
-
-    // ...ここには |nsIFoo| を必要としない多くの事柄があります。
-
-    return rv;
-  }
-
-
-

編集者注: この議論を 効率性のセクションへ移動し、ここからリンクを張ります。 -

-

nsCOMPtr のリークを作るのでしょうか (デバッグテストのため) ?

-

生の [XP]COM インタフェースポインタを「入出力」パラメタとして使う getter を呼べばよいのでしょうか?

-

nsIFoo*& パラメタで設定する getter を呼べばよいのでしょうか?

-

その結果を AddRef しない getter を呼べばよいのでしょうか?

-

インタフェースポインタを返すどの {{ mediawiki.external('XP') }}COM 関数 (getter) も、そのポインタに対して AddRef をすでに呼び出していなければいけません。 もしそれが行われないのであれば、おそらくそれをバグとして報告すべきでしょう。 この問題を解決するのに使うどのコードパターンに対しても、このようにコメントすべきです。 例えば、// 警告: この getter は 結果を AddRef() していません。 もし getter が新しいポインタを関数の復帰値として返すのであれば、心配いりません。 -

-

一般

-

nsCOMPtr は、コードを膨張させますか?

-

nsCOMPtr は速いですか? タイトなループの中でも使えますか?

-

参考文献一覧

-

Web リソース

- -

書籍

- -

人々

- -
-
-{{ languages( { "en": "en/Using_nsCOMPtr/Frequently_Asked_Questions" } ) }} diff --git a/files/ja/mozilla/tech/xpcom/using_nscomptr/getting_started_guide/index.html b/files/ja/mozilla/tech/xpcom/using_nscomptr/getting_started_guide/index.html deleted file mode 100644 index 826f85c35a..0000000000 --- a/files/ja/mozilla/tech/xpcom/using_nscomptr/getting_started_guide/index.html +++ /dev/null @@ -1,294 +0,0 @@ ---- -title: Getting Started Guide -slug: Mozilla/Tech/XPCOM/Using_nsCOMPtr/Getting_Started_Guide -tags: - - XPCOM -translation_of: Mozilla/Tech/XPCOM/Using_nsCOMPtr/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 の所有のガイドライン は、所有権が必要になった時に、いくつかヒントを与えてくれます。以下のリストは、開始地点としていいですが、しかし決して完全ではありません。

-

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

- -

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

- -

これらにより、参照カウントをプログラマが手動で正しくするのは、大変であることが分かります。それは、簡単そうに見えますが、しかし実際には 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 を適用しないように注意してください。[説明のために この短いセクション を見てください。]

-

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

- -

 

diff --git a/files/ja/mozilla/tech/xpcom/using_nscomptr/index.html b/files/ja/mozilla/tech/xpcom/using_nscomptr/index.html deleted file mode 100644 index 5d821dfeca..0000000000 --- a/files/ja/mozilla/tech/xpcom/using_nscomptr/index.html +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: Using nsCOMPtr -slug: Mozilla/Tech/XPCOM/Using_nsCOMPtr -tags: - - XPCOM -translation_of: Mozilla/Tech/XPCOM/Using_nsCOMPtr ---- -

このドキュメントは、nsCOMPtr について書かれたすべてを集めたものです。もしあなたが nsCOMPtr について疑問があり、このドキュメントでは分からないのであれば、 おそらくそれに答えられるドキュメントはないでしょう。XPCOM ニュースグループ、または他のベテランの nsCOMPtr ユーザに尋ねる、あるいは実験して答えを見つける、などの別の手段で答えを得るしかありません。

もしあなたがまだ nsCOMPtr 使ったことがないのであれば、ここは始めるのに適した場所です。そのまま読み進んでください。いつ止めるかは、あなた次第です。nsCOMPtr をしばらく使った後で、未知の領域に到達したり、コンパイルエラーになったりしたら、このドキュメントに戻ってきて、リファレンスマニュアル、あるいは FAQ から助けを得たいと思うでしょう。 -

-

目次

-
  1. 状況、最近の更新と計画 -
  2. スタートガイド -
  3. リファレンスマニュアル -
  4. よく聞かれる質問 -
-
-

原文書の情報

- -
-
-
diff --git a/files/ja/mozilla/tech/xpcom/using_nscomptr/reference_manual/index.html b/files/ja/mozilla/tech/xpcom/using_nscomptr/reference_manual/index.html deleted file mode 100644 index c5c82f9fdd..0000000000 --- a/files/ja/mozilla/tech/xpcom/using_nscomptr/reference_manual/index.html +++ /dev/null @@ -1,523 +0,0 @@ ---- -title: Reference Manual -slug: Mozilla/Tech/XPCOM/Using_nsCOMPtr/Reference_Manual -tags: - - XPCOM -translation_of: Mozilla/Tech/XPCOM/Using_nsCOMPtr/Reference_Manual ---- -

-

このセクションは、あなたが、すでに nsCOMPtr に慣れ親しんでいるけれども、もっと詳細な事柄を知りたい場合に役立つでしょう。もし、まだ nsCOMPtr を前に使っていないのであれば、まず スタートガイド を読みたいかもしれません。もし、壊れたビルドを直そうとしているのであれば、FAQ によって、もっと素早く答えを得られるかもしれません。 -

-

基本

-

設計

-

nsCOMPtr は、所有する参照として使われる所で、生の [XP]COM インタフェースポインタを完全に置き換えるように設計されました。生の [XP]COM インタフェースポインタを使うことができるところであれば、ほとんどの場合、nsCOMPtr を使うことができるはずです。nsCOMPtrは、生の [XP]COM インタフェースポインタと正確な同じ大きさと形です。それは、容量を食いすぎることもなく、メンバ変数として使うことができます。 -

所有する参照のほとんどの作業は、nsCOMPtr のコンストラクタ、デストラクタと代入演算子で行われます。あなたが(代入や初期化により) nsCOMPtr を異なる [XP]COM オブジェクトで「指す」場合、もし古い値があれば、それを Release しなければなりません。そして、新しい値を AddRef しなければなりません。自分のデストラクタ実行時には、同様に Release しなければなりません。nsCOMPtr は、もしあなたがいつも正しいことを覚えているのであれば、ちょうどあなたがしていた作業しかしません。 -

-

安全性の特徴

-
型の保護手段
-

元の型のための正しい [XP]COM インタフェースポインタを保持しているのは、nsCOMPtr の不変性です。例えば、nsCOMPtr<nsIFoo> は、[XP]COM オブジェクトに nsIFoo インタフェースを問い合わせる時に、code>QueryInterface</code> によって返されるポインタを常に保持しています。デバッグビルドでは、もし代入時に QueryInterface を呼ばずに、この不変性を覆すと、nsCOMPtr は、間違った代入として実行時にアサートするでしょう。 -

- - -
-
// 二つの無関係なインタフェース |nsIFoo| と |nsIBar| があるものとします...
-nsIBar* bar = ...;
-// ...
-
-nsCOMPtr<nsIFoo> foo = bar;
-  // NS_ASSERTION: "QueryInterface needed"
-
-  // ...あなたは、この行をコンパイルできるとみなすかもしれませんが
-  // (キャストでもそうです。なぜなら型が C に関係づけられているからです)
-
-
-

この不変性は、nsCOMPtr<nsISupports> では、緩められます。nsISupports*(あるいはvoid*)のように、人々は一般的にnsCOMPtr<nsISupports> を「任意の [XP]COM インタフェース」とみなして使います。もし実際の型を気にしないようなオブジェクトに対して、nsCOMPtr が [XP]COM として正しい nsISupportsQueryInterface することを強制するのであれば、煩わしいかもしれません。 -

-
NULL 間接参照の保護手段
-

もし中が空の時に間接参照しようとすると、nsCOMPtr は、実行時にアサートします。例えば、 -

- - -
-
nsCOMPtr<nsIFoo> foo;
-  // 注: デフォルトでは、|0| に初期化されます。
-
-foo->DoSomething();
-  // NS_PRECONDITION: "You can't dereference a NULL nsCOMPtr with operator->()"
-
-
-

同様の事前条件が operator* のために、介在します。 -

-
参照カウントの保護手段
-

nsCOMPtr から元の生のポインタを取り出すすべての操作に対して、安全な特徴を実装するための C の別のトリックを使います。返ってきたポインタに対して、AddRefReleasedeleteを実行することができません。 -

- - -
-
nsCOMPtr<nsIFoo> foo = ...;
-
-foo->AddRef();     // エラー: |AddRef| はプライベートです。
-delete foo.get();  // エラー: |operator delete| はプライベートです。
-NS_RELEASE(foo);   // エラー: |Release| はプライベートです。
-
-
-

もちろん、nsCOMPtr によって提供される安全性に関する最も重要な特徴は、それが適切な時期に自動的に AddRefRelease を実行することです。 -

-

キャスト

-

nsCOMPtr では、旧式の C のキャストを使わないでください。旧式のキャストは、たとえそれが正しくないとしても、コンパイルできることが保障されています。旧式のキャストは、もし変換が定義されていないのであれば、reinterpret_cast と同じものにしてしまいます。そのようなキャストは、nsCOMPtr の機構を容易にバイパスしてしまい、リークの発生、型の不一致、その他の不幸な出来事を招きます。 -

- - -
-
// 旧式の C のキャストが |nsCOMPtr| 機構をバイパスし、リークを発生させる...
-
-nsresult rv;
-nsCOMPtr<nsIFoo> foo = ...;
-
-// ...
-rv = GetFoo( (nsIFoo**)&foo );
-rv = GetFoo( &(nsIFoo*)foo );
-  // もちろん、これらはコンパイルできます。でもリークしてしまいます。
-
-
-

これを防ぐのを助けるために、上記のように operator&private にすることにより、最初の形を不法とすることを試みます。バグ 59414 を参照してまださい。 -

-

実装の詳細とデバッグ機構

-

nsCOMPtr はクラスですが、virtual なメソッドを持っていません。つまり、vtable または vptr を持っていません。キーとなるいくつかのルーチンが共通の非テンプレートの基底クラスに分解されるので、実際の元のポインタは、nsISupports* として保存されます (ただしデバッグビルドで NSCAP_FEATURE_DEBUG_PTR_TYPES がオンになっている場合は除きます)。それは、これらの分解されたルーチンのために、nsCOMPtr のユーザが XPCOM ライブラリとリンクしなければいけないからです。 -

NSCAP_FEATURE_DEBUG_PTR_TYPES がオンになっている時、nsISupports* 型の変数に元のポインタを保持する代わりに、nsCOMPtr は、元の型に適合するポインタに保持します。これにより、ソースレベルのデバッガがより簡単にポインタを「追跡」できるようになります。しかしながら、基底クラスに分解されるルーチンは、今やテンプレート特有のインラインコードとなります。分解される基底クラスはありません。これは、すべてのアプリケーションが NSCAP_FEATURE_DEBUG_PTR_TYPES について同じ設定でコンパイルされなければならないことを意味します。そうでないと、いくつかの部分では基底クラスを期待し、他の部分ではそうでないことを期待することになります。アプリケーションは、リンクできないでしょう。 -

-

ユニットテスト

-

nsCOMPtrのためのユニットテストは、このファイルにあります。 -

- -

初期化と代入

-

組み込み形式

-

nsCOMPtr への代入や初期化は、簡単に理解できます。nsCOMPtr は、その古い値がもしあれば Release し、そして新しい値を代入し、AddRef を呼び出し、および/または、あなたが直接「注釈」することによって dont_AddRef のような指示子で代入する QueryInterface を呼び出します。このセクションでは、それぞれ起こりうる場合を記述します。ただし、指示子については、より簡潔に以下の表で記述しています。 -

あなたは、nsCOMPtr を以下のものから構築するか、以下のものから代入することができます。 -

- -

最初の三つは、単純で明らかです。4 番目のもの (dont_QueryInterface 指示子の適用) は、同じ型の生の [XP]COM インタフェースポインタの代入と同じことです。残りの指示子は、特殊な状況において、いくつかの付加的な制御を提供するものです。さらに、nsCOMPtr を初期値なしで構築することができ、その場合は、0 で初期化されます。ちょうどプリミティブなポインタのように、値が0nsCOMPtr は、どのオブジェクトも指しません。そして、if (foo)if (!foo) のように式をテストすることができます。 -

上述した指示は、この表によりもっとはっきりするでしょう。 -

- - - - - - - -
表 1. nsCOMPtrに代入するオプション -
- QueryInterface しない - QueryInterface する -
AddRef する - -

T*,
dont_QueryInterface(T*)
-

-
-

do_QueryInterface(nsISupports*),
do_QueryInterface(nsISupports*, nsresult*)
do_QueryReferent(nsIWeakReference*),
do_QueryReferent(nsIWeakReference*, nsresult*)
-

-
AddRef しない - -

dont_AddRef(T*),
getter_AddRefs(T*)
-

-
-

n/a -

-
-

例えば、nsCOMPtr への代入において、(なんらかの理由ですでに AddRef を実行したために)代入するポインタに対して AddRef を実行したくない場合、「AddRefしない」と「QueryInterface しない」の交差する所にある dont_AddRef(T*) を使うのは一つの可能性です。以下に、dont_AddRefを使い、様々な位置にそれらの「注釈」が表れるサンプルを示します。 -

- - -
-
// |nsCOMPtr| への代入を管理する...
-
-  // コンストラクタにおいて...
-nsCOMPtr<nsIFoo> foo1( dont_AddRef(rawFoo1Ptr) );
-nsCOMPtr<nsIFoo> foo2 = dont_AddRef(rawFoo2Ptr);
-   // (直接の初期化と呼ばれる) 関数の形式と (コピーの初期化と呼ばれる)
-  //  コンストラクタの代入の形式は、微妙に異なる意味を
-  //  持つことに注意してください。直接の初期化の方が好ましい。
-
-nsCOMPtr<nsIFoo> foo3;
-
-  // 通常の代入において...
-foo3 = dont_AddRef(rawFoo3Ptr);
-
-   // 表で記述されている注釈をコンストラクタと
-  //  単純で古いタイプの代入に適用しています。
-
-
-

表に示されたどの注釈も dont_AddRef() を使って示されたすべての場所に出現可能です。続くセクションでそれぞれの可能性を記述します。 -

-
nsCOMPtr<T> = T*,
nsCOMPtr<T> = dont_QueryInterface( T* )
-

表で T* として示されるデフォルトの振舞いでは、新しい値に対して、AddRef を実行します。しかし、それに対して、QueryInterface は実行しません。「注釈」がない時に何が起きるかという例を示します。例えば、 -

- - -
-
nsCOMPtr<nsIFoo> foo( aFooPtr ); // もしくは
-foo = aFooPtr;
-  // ...|AddRef| は呼び出しますが、|QueryInterface| は呼び出しません。|
-
-  // 同じものをより明示的に表すと...
-nsCOMPtr<nsIFoo> foo( dont_QueryInterface(aFooPtr) ); // もしくは
-foo = dont_QueryInterface(aFooPtr);
- 
-
-
-

この形式を使うことにより、あなたが代入しているポインタがすでに、nsCOMPtr の元の型、この場合は、nsIFoo に適合している、[XP]COMとして正しいインタフェースへのポインタであることを約束していることになります。 -

-
nsCOMPtr<T> = do_QueryInterface( nsISupports* ),
nsCOMPtr<T> = do_QueryInterface( nsISupports*, nsresult* )
-

もし、上記の約束を果たせない時は、nsCOMPtr に対して、代入において QueryInterface を呼び出す必要があると、「注釈」をすることができます。例えば、 -

- - -
-
nsCOMPtr<nsIFoo> foo( do_QueryInterface(aBarPtr) ); // もしくは
-foo = do_QueryInterface(aBarPtr);
-   // ...|QueryInterface| が呼ばれる _でしょう_。(その結果 |AddRef| も呼ばれます)
-
-  // もちろん、|QueryInterface| を呼んでいるので、
-  //  エラー結果も必要になるでしょう...
-nsresult rv;
-nsCOMPtr<nsIFoo> foo( do_QueryInterface(aBarPtr, &rv) ); // もしくは
-foo = do_QueryInterface(aBarPtr, &rv);
- 
-
-
-
nsCOMPtr<T> = dont_AddRef( T* ),
nsCOMPtr<T> = getter_AddRefs( T* )
-

時々、すでに AddRef が実行されたポインタをたまたま持っていて、それを nsCOMPtr に代入したい場合があるでしょう。これは、しばしば、(nsresult を結果とするのではなく) AddRef が実行されたポインタを結果として返す getter を使った時に起きます。あるいは、効率性のための変形により起きる場合もあります。dont_AddRef は、このような場合の完璧な治療法です。 -

- - -
-
nsIFoo* temp;
-nsresult rv = GetFoo(&temp);
-nsCOMPtr<nsIFoo> foo( dont_AddRef(temp) );
-  // |temp| はすでに |AddRef| を実行済ですが、我々はこれを
-  //  |nsCOMPtr| で管理しようとしています。.
-
-nsCOMPtr<nsIFoo> foo( getter_AddRefs(CreateAFoo()) );
-  // |getter_AddRefs| は |dont_AddRef| の同意語です。
-  //  これは、|AddRef| が実行されたポインタを返す関数に適用する時に分かりやすくするものです。
-
-nsCOMPtr<nsIFoo> foo( dont_AddRef(CreateAFoo()) );
-  // あるいは、あなたはそれが好きではないかもしれません...
-
-
-
nsCOMPtr<T> = /* QueryInterface を呼び出しますが、AddRef は呼び出しません。 */
-

表のこの象限が「n/a (not applicable)」とマークされているのに気づくでしょう。「QueryInterface を呼び出すが、AddRef を行わないこと」を意味する明示的な指令はありません。このオプションは、間違った型のオブジェクトを返す getter を呼び出す状況に対応します。すでに AddRef を実行したオブジェクトを持っているので、もう AddRef を実行したくないが、違うインタフェースを得る必要がある場合です。それはできません。QueryInterface は、常に AddRef をその結果に対して実行します。そして、正しい型を得るための QueryInterface の呼び出しの代用品は存在しません。解決するには、2 段階のプロセスを実行します。 -

- - -
-
// ...
-
-  // getter は (間違った型の) すでに |AddRef| を実行したオブジェクトを返します...
-nsCOMPtr<nsIBar> bar( getter_AddRefs(CreateBar()) );
-  // ...(このオブジェクトに対して) 正しい型を問い合わせる必要があります。
-nsCOMPtr<nsIFoo> foo( do_QueryInterface(bar) );
-
-
-

この場合において、人々が陥る不運なワナは、getter 関数が結果を AddRef していることを忘れることです。こんな感じのコードをタイプしてしまいます: -

- - -
-
nsCOMPtr<nsIFoo> foo( do_QueryInterface(CreateBar()) );
-  // おっと! |CreateBar| によって返ってくるインタフェースがリークしてしまいます。
-  //  この場合、あなたは上に示した二つのステップの解決法で処理する_必要_があります。
-
-  // ありそうもないですか? こんな感じの形で見ることはあるでしょう。
-nsCOMPtr<nsIFoo> foo( do_QueryInterface(aList->ElementAt(i)) );
-  // すべての良い getter のように、|ElementAt| は、
-  // インタフェースからの必要性に応じて問い合わせを行なった後、
-  // 破棄されるかも知れない結果を |AddRef| します。
-
-
-

Bugzilla バグ 8221 は、この特定のリークの発見と修正に限定されたものです。 -

-

nsCOMPtr ヘルパー

-
nsCOMPtr<T> = do_QueryReferent( nsIWeakReference* ),
nsCOMPtr<T> = do_QueryReferent( nsIWeakReference*, nsresult* )
-

nsIWeakReference に基づく弱い参照を容易にする do_QueryReferent というのがあります。nsIWeakReference は、他のオブジェクトのプロキシとして振舞う [XP]COM オブジェクトです。nsIWeakReference と (上記の) 他のオブジェクトは、特別な関係にあります。それらは、お互いのことを知っています。しかし、どちらももう一方への所有する参照を保持していません。二つのオブジェクトは、もう一方へのダングリングポインタを持たないことを保障するように協調しています。nsIWeakReference オブジェクトにおいて所有する参照を保持することにより、この他のオブジェクトを必要な時に得ることができ、しかし、それ (他のオブジェクト) が生きていなくてもよいのです。そのオブジェクトを得るためには、nsIWeakReference オブジェクトに、あなたの代わりに QueryInterface するように依頼します。もしオブジェクトがまだ存在しており、要求されたインタフェースをサポートしているのであれば、あなたは (できれば、一時的に) それに対する所有する参照を持つことができます。 -

- - -
-
nsIWeakReference* weakPtr = ...;
-
-weakPtr->QueryReferent(
-
-
-

T* として nsCOMPtr<T> を使う

-

nsCOMPtrをポインタとして使う

-

「入力」パラメタ

-

「出力」パラメタ: getter_AddRefs

-

nsCOMPtr への代入は、とても理解しやすいです。nsCOMPtr は、古い値がもしあれば、それを Release します。そして、代入した新しい値を AddRef し、および/または上述した指令に示されたQueryInterface を呼び出します。これらの規則は、nsCOMPtr として宣言されたパラメタや関数の復帰値のコピーにおいて起こる「代入」でも同じく適用されます。もし nsCOMPtr を生の [XP]COM インタフェースポインタの実用的な代用品としたいのであれば、しかしながら、「出力」パラメタの問題に対処する必要があります。多くの [XP]COM 関数は、結果のインタフェースポインタをパラメタを通じて返します。例えば、 -

- - -
-
// Getter は、インタフェースポインタを「出力」パラメタを通じて返すことができます。...
-
-nsresult GetFoo( nsIFoo** );     // 標準的 getter
-nsresult GetFoo2( nsIFoo*& );    // 非標準的 getter
-nsresult GetSomething( void** ); // 「型無し」の getter
-  // 注: |QueryInterface| は、「型無し」の getter の例です。
-
-
-

我々は、「出力」パラメタを使うルーチンへポインタや参照によって nsCOMPtr を渡せなければいけません。問題は、getter 内部には、nsCOMPtr に対する情報がないことです。それは、生の [XP]COM インタフェースポインタへのポインタ (または参照) を得ていると考えます。nsCOMPtr のスマートな代入演算子は、呼ばれません。古い値があれば、リークしてしまいます。 -

ここで、getter_AddRefs( nsCOMPtr& )が役に立ちます。getter_AddRefs は、古い値があれば Release し、それをクリアします。そして、それに対するポインタを返し、getter は nsCOMPtrAddRef を実行した新しい値を設定します。我々は、これらの状況で、生の [XP]COM インタフェースポインタに適用していた & を置き換えるものとして、getter_AddRef を使用します。getter_AddRefs は、通常 nsCOMPtr のコンストラクタと代入演算子から得ていた魔法を詰めこんだものです。 -

- - -
-
// 生の [XP]COM インタフェースポインタ...
-
-nsIFoo* foo;
-
-GetFoo(&foo);
-GetFoo2(foo);
-GetSomething((void**)&foo);
-
-
-
// |nsCOMPtr|...
-
-nsCOMPtr<nsIFoo> foo;
-
-GetFoo(getter_AddRefs(foo));
-GetFoo2(*getter_AddRefs(foo));
-GetSomething(getter_AddRefs(foo));
-
-
-

これを実現するのに、なぜ単に operator& をオーバーロードしないのでしょうか? いくつかの理由: 他の状況では、nsCOMPtr のアドレスを取るのは、不便なことになります。「getter_AddRefs」という名前は、getter としてある一定の振舞いを強制します。そして、かつては、他の可能性がありました (あなたがまさに学ぼうとしているように)。 -

パラメタを通じて復帰値を返しますが、AddRef を実行していない getter のために、getter_doesnt_AddRef( nsCOMPtr& ) というのは、ありますか? いいえ、ありません。かつてありましたが、それは 3 つの理由でなくなりました: -

- - - -
-
// その結果に対して、|AddRef| を (違法に) 呼び出さない getter を呼び出します...
-
-nsIFoo* temp;
-nsresult rv = GetFoo_WithoutAddRef(&temp);
-   // 自分への注: |GetFoo_WithoutAddRef| をバグとして報告しなければならない。
-  // すべての getter は、 |AddRef| しなければならない。
-nsCOMPtr<nsIFoo> foo = temp;
-
-
-

「入出力」パラメタ

-

「入力/出力」パラメタについては、どうなんでしょう? -

-

効率性と正確性

-

nsCOMPtrのコスト

-

nsCOMPtr は、生の [XP]COM インタフェースポインタに対する実用的な置き換えとなるべく調整されています。所有する参照として使うのであれば、どの場所においてもです。nsCOMPtrs のパフォーマンスについては一般的に、スペース的には少し効率がよく、時間的には、ごくわずかに効率が悪いです。パフォーマンスに関することにより、nsCOMPtr を使わないのは良くありません。このセクションを通じて提示するパターンがnsCOMPtr からより多くのものを引き出すのを手伝ってくれるでしょう。 -

-
スペース
-

一般的に、nsCOMPtrは、生の [XP]COM ポインタを使うよりもスペース的には、効率がよくなる可能性があります。これは主にそのデストラクタの分解とより複雑なコンストラクタと代入演算子のためです。このセクションの最適化の tips に従うことで、生のポインタで使用するのよりもオブジェクトの生成するバイトがより少ないコードを書くことができるでしょう。これらの忠告に従わないとしても、nsCOMPtr のコードは、依然として、より小さくなり、あるいは最悪でも生のポインタ版よりもごくわずか増えるだけです。詳細については、Code Bloat [長文、要約が最初にあります] を見てください。もっとも、ここでは、そのドキュメントからの推奨事項を繰り返しています。 -

-
時間
-

[[もっと時間パフォーマンスの測定が必要です。]] -

二つ以上のサブルーチン、すなわち AddRefReleaseQueryInterface が必要な場所では、いくつかの nsCOMPtrル ーチンが分解され、そのためサブルーチンを呼び出しと対応する付加的な時間を必要とします。この時間は、特に QueryInterface により行われる作業や Release により行われるかもしれない作業にもかかわらず、極わずかです。 -

その他のすべての場合、nsCOMPtr は手で行われる作業をするだけです。nsCOMPtr が使用される作業の大部分は、operator-> での間接参照であり、原始的なポインタが行うものと同じです。この操作は、すべてのプラットフォームで、生の [XP]COM インタフェースポインタでの操作とちょうど同じコードを作り出し、そして同じ時間を消費します。デストラクタは、クライアントコードが生の [XP]COM インタフェースポインタに対して、Release を呼び出すのに対応しますが、処理が分解されているため、サブルーチンを呼び出すための余分な時間が必要となります。もっとも、これは、delete を実行するかもしれない Release の呼び出しと参照する方のデストラクタの両方の場合においてすでに存在するコストに対して、バランスが取れています。すべての nsCOMPtr のコンストラクタと代入演算子は、インラインです。単純なコンストラクタ、すなわち問い合わせをしないもの、は、あなたが手で書いたものと同じ作業だけを行います。AddRefReleaseQueryInterface の中の一つ以上の呼び出しを行うすべてのルーチンは、分解されており、そのため、サブルーチン呼び出しの余分なコストを含んでいます。 -

いくつかのルーチンが分解されているという事実により、余分なサブルーチン呼び出しのオーバーヘッドが生じます。そして、その事実および初期化がバイパスできないという事実によって、生の [XP]COM インタフェースポインタに対して、nsCOMPtr が余分な実行時間のコストを生じるのです。スペースと時間のトレードオフは、nsCOMPtr において見事にバランスが取れています。分解されたルーチンは、膨張に関する測定の直接的な結果です。 -

-

代入の好ましい構築

-

時間においてもスペースにおいても、nsCOMPtr への値の代入の最も効率的な方法は、構築時におけるものです。合理的である限り、代入しながら構築する方が好ましいです。nsCOMPtr のメンバをコンストラクタのメンバ初期化節で初期化すべきです。 -

- - -
-
// |nsCOMPtr| のメンバをコンストラクタの
-// メンバ初期化節で初期化する...
-
-class Bar
-  {
-    public:
-      Bar( nsIFoo* initial_fooPtr );
-      // ...
-    private:
-      nsCOMPtr<nsIFoo> mFooPtr;
-  };
-
-Bar::Bar( nsIFoo* initial_fooPtr )
-    : mFooPtr(initial_fooPtr) // _ここで_ 初期化します
-  {
-    // ここではありません。
-  }
-
-
-

付け加えておくと、代入の形式を構築の形式に変換する一時的オブジェクトを使う、最適化のパターンがあります。 -

- - -
-
// 後に代入が続くデフォルトの
-// 構築は非効率的です...
-
-nsCOMPtr<nsIFoo> foo;
-nsresult rv=GetFoo(getter_AddRefs(foo));
-
-
-
-
-
-
-
-
// ...構築だけをします。
-
-nsIFoo* temp;
-nsresult rv=GetFoo(&temp);
-nsCOMPtr<nsIFoo> foo=dont_AddRef(temp);
-
-  // この「生のポインタ、getter の呼び出し、
-  // |dont_AddRef| の代入」パターンを覚えて
-  // ください。それは、多くの効率に関する
-  // 議論で出てきたものです。
-
-
-

どちらの場合も、あなたは、正当な nsCOMPtr で、その値として GetFoo の結果が設定された foo というオブジェクト、および GetFoo により返された rv という状態を得ます。しかしながら、一時的変数を使う場合は、nsCOMPtr への値の設定をする構築を使っており、(ソース上では、少し複雑になっていますが)、代入に続くデフォルトの構築よりは、効率的になっています。そして、より簡単な例によって、このイベントの過程は理解されるでしょう。 -

-

代入における好ましい破壊

-

QueryInterface の呼び出しよりも do_QueryInterface の方が望ましいです。

-

繰り返し

-

これは、普通のポインタでデータ構造の繰り返しをする時の共通のイディオムです。例えば、 -

- - -
-
// [XP]COM オブジェクトでないものに対してポインタで繰り返しを行う...
-
-Node* p = ...;
-while ( p )
-  {
-    // ...
-    p = p->next;
-  }
-
-
-

同様に、このパターンが for ループとしても表現されるのをしばしば見かけます。しかしながら、これを生の [XP]COM インタフェースポインタに対して行うとどうなるか、考えてみてください。 -

- - -
-
// 生の [XP]COM インタフェースポインタで繰り返しを行います...
-
-nsIDOMNode* p = ...;
-while ( p )
-  {
-    // ...
-    p->GetNext(&p);
-      // 問題です! |p| を |Release| せずに上書きしてしまいました。
-  }
-
-
-

おっと! p に対して、新しいポインタを設定する前に、Release し損ねてしまいました。みんながこれを多く行うため、これが通常の [XP]COM コードのリークの大きな原因となってしまいました。では、代わりにこうすることはできるのでしょうか ? -

- - -
-
// 生の [XP]COM インタフェースポインタで繰り返しを行います...
-
-nsIDOMNode* p = ...;
-while ( p )
-  {
-    // ...
-    NS_RELEASE(p);
-    p->GetNext(&p);
-      // 問題です! ダングリングしているか |NULL| であるポインタの
-      // メンバ関数を呼ぼうとしています。
-  }
-
-
-

残念ながらダメです。Release した後、nsCOMPtr は、ダングリングしている状態になるかもしれません。実は、NS_RELEASE マクロを使うと、p は、GetNext を呼び出すまでは、NULL になるでしょう。 -

では、同じことを nsCOMPtr で書いてあると想像してみてください。 -

- - -
-
// |nsCOMPtr| で繰り返しを行います...
-
-nsCOMPtr<nsIDOMNode> p = ...;
-while ( p )
-  {
-    // ...
-    p->GetNext( getter_AddRefs(p) );
-      // 問題です! |NULL| ポインタを通じてメンバ関数を呼び出そうとしました。
-  }
-
-
-

ここでは、nsCOMPtr の使用は、生の [XP]COM インタフェースポインタの使用とほとんど同じです。getter_AddRefs は、 Release し、そしてそれに代入する前に p をクリアします。すなわち、GetNext が呼ばれる前にそれを行います。これは、GetNext の呼び出しを行う前に、NULL ポインタを通じて、呼び出そうとしてしまうことを意味します。生の [XP]COM インタフェースポインタと違い、nsCOMPtr は、盲目的に NULL ポインタを通じて GetNext を呼び出そうとする代わりに、assert を実行します。 -

これは問題です。では、解決法は、なんでしょうか。もしこれが生の [XP]COM インタフェースであれば、おそらく一時的変数を導入するでしょう。我々は、nsCOMPtrで同じことをすることができます。 -

- - -
-
// 生の [XP]COM インタフェースポインタ
-// での安全な繰り返し...
-
-nsIDOMNode* p = ...;
-while ( p )
-  {
-    // ...
-
-      // 一時的変数を導入することで、
-      // |p| で足踏みをすることもありません。
-    nsIDOMNode* temp = p;
-    temp->GetNext(&p);
-    NS_RELEASE(temp);
-  }
-
-
-
// |nsCOMPtr| での安全な繰り返し...
-
-
-nsCOMPtr<nsIDOMNode> p = ...;
-while ( p )
-  {
-    // ...
-
-      // 一時的変数を導入することで、
-      // |p| で足踏みをすることもありません。
-    nsCOMPtr<nsIDOMNode> temp = p;
-    temp->GetNext(getter_AddRefs(p));
-  }
-
-
-
-

nsCOMPtr をパラレルにするのは、容易に理解できますが、生のポインタの枠組に比べて、余分な AddRefRelease を一回ずつしなければなりません。少し変形することで、コードは見づらくなりますが、(おそらく、ごくわずかですが) より効率的になります。 -

- - -
-
// 安全で、効率的な、|nsCOMPtr| での繰り返し...
-
-nsCOMPtr<nsIDOMNode> p = ...;
-while ( p )
-  {
-    // ...
-    nsIDOMNode* next;
-    p->GetNext(&next);
-    p = dont_AddRef(next);
-  }
-
-  // 見てください! これはおなじみの「生のポインタ、getterの呼び出し、
-  // |dont_AddRef| の代入」パターンです。
-
-
-

getter を書く

-

コンパイラの悩みの種

-
-
diff --git a/files/ja/mozilla/tech/xpcom/using_nscomptr/status,_recent_changes,_and_plans/index.html b/files/ja/mozilla/tech/xpcom/using_nscomptr/status,_recent_changes,_and_plans/index.html deleted file mode 100644 index 590fcab98c..0000000000 --- a/files/ja/mozilla/tech/xpcom/using_nscomptr/status,_recent_changes,_and_plans/index.html +++ /dev/null @@ -1,36 +0,0 @@ ---- -title: 'Status, Recent Changes, and Plans' -slug: 'Mozilla/Tech/XPCOM/Using_nsCOMPtr/Status,_Recent_Changes,_and_Plans' -tags: - - XPCOM -translation_of: 'Mozilla/Tech/XPCOM/Using_nsCOMPtr/Status,_Recent_Changes,_and_Plans' ---- -

-

このセクションが、初めて読む人に邪魔になって申し訳ありません。 目次にとばしたり、直接 スタートガイドリファレンスマニュアルFAQ に行きたいかもしれません。 このセクションは、最近の更新をチェックするのが簡単になるようにトップに設けています。 -


-

-

nsCOMPtr に対する最近の更新

-

新しいものから順に -

- -

nsCOMPtr の計画

- -

最近のドキュメントの更新

- -
-
-- cgit v1.2.3-54-g00ecf