diff options
Diffstat (limited to 'files/ja/mozilla/tech/xpcom/using_nscomptr/reference_manual/index.html')
-rw-r--r-- | files/ja/mozilla/tech/xpcom/using_nscomptr/reference_manual/index.html | 523 |
1 files changed, 523 insertions, 0 deletions
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 new file mode 100644 index 0000000000..c5c82f9fdd --- /dev/null +++ b/files/ja/mozilla/tech/xpcom/using_nscomptr/reference_manual/index.html @@ -0,0 +1,523 @@ +--- +title: Reference Manual +slug: Mozilla/Tech/XPCOM/Using_nsCOMPtr/Reference_Manual +tags: + - XPCOM +translation_of: Mozilla/Tech/XPCOM/Using_nsCOMPtr/Reference_Manual +--- +<p> +</p><p>このセクションは、あなたが、すでに <code>nsCOMPtr</code> に慣れ親しんでいるけれども、もっと詳細な事柄を知りたい場合に役立つでしょう。もし、まだ <code>nsCOMPtr</code> を前に使っていないのであれば、まず <a href="ja/Using_nsCOMPtr/Getting_Started_Guide">スタートガイド</a> を読みたいかもしれません。もし、壊れたビルドを直そうとしているのであれば、<a href="ja/Using_nsCOMPtr/Frequently_Asked_Questions">FAQ</a> によって、もっと素早く答えを得られるかもしれません。 +</p> +<h3 id=".E5.9F.BA.E6.9C.AC" name=".E5.9F.BA.E6.9C.AC"> 基本 </h3> +<h4 id=".E8.A8.AD.E8.A8.88" name=".E8.A8.AD.E8.A8.88"> 設計 </h4> +<p><code>nsCOMPtr</code> は、所有する参照として使われる所で、生の [XP]COM インタフェースポインタを完全に置き換えるように設計されました。生の [XP]COM インタフェースポインタを使うことができるところであれば、ほとんどの場合、<code>nsCOMPtr</code> を使うことができるはずです。<code>nsCOMPtr</code>は、生の [XP]COM インタフェースポインタと正確な同じ大きさと形です。それは、容量を食いすぎることもなく、メンバ変数として使うことができます。 +</p><p>所有する参照のほとんどの作業は、<code>nsCOMPtr</code> のコンストラクタ、デストラクタと代入演算子で行われます。あなたが(代入や初期化により) <code>nsCOMPtr</code> を異なる [XP]COM オブジェクトで「指す」場合、もし古い値があれば、それを <code>Release</code> しなければなりません。そして、新しい値を <code>AddRef</code> しなければなりません。自分のデストラクタ実行時には、同様に <code>Release</code> しなければなりません。<code>nsCOMPtr</code> は、もしあなたがいつも正しいことを覚えているのであれば、ちょうどあなたがしていた作業しかしません。 +</p> +<h4 id=".E5.AE.89.E5.85.A8.E6.80.A7.E3.81.AE.E7.89.B9.E5.BE.B4" name=".E5.AE.89.E5.85.A8.E6.80.A7.E3.81.AE.E7.89.B9.E5.BE.B4"> 安全性の特徴 </h4> +<h5 id=".E5.9E.8B.E3.81.AE.E4.BF.9D.E8.AD.B7.E6.89.8B.E6.AE.B5" name=".E5.9E.8B.E3.81.AE.E4.BF.9D.E8.AD.B7.E6.89.8B.E6.AE.B5"> 型の保護手段 </h5> +<p>元の型のための正しい [XP]COM インタフェースポインタを保持しているのは、<code>nsCOMPtr</code> の不変性です。例えば、<code>nsCOMPtr<nsIFoo></code> は、[XP]COM オブジェクトに <code>nsIFoo</code> インタフェースを問い合わせる時に、code>QueryInterface</code> によって返されるポインタを常に保持しています。デバッグビルドでは、もし代入時に <code>QueryInterface</code> を呼ばずに、この不変性を覆すと、<code>nsCOMPtr</code> は、間違った代入として実行時にアサートするでしょう。 +</p> +<table> +<tbody><tr> +<td> +<pre class="eval"><span class="comment">// 二つの無関係なインタフェース |nsIFoo| と |nsIBar| があるものとします...</span> +nsIBar* bar = ...; +<span class="comment">// ...</span> + +<span class="warning">nsCOMPtr<nsIFoo> foo = bar;</span> + <span class="comment">// NS_ASSERTION: "QueryInterface needed"</span> + + <span class="comment">// ...あなたは、この行をコンパイルできるとみなすかもしれませんが</span> + <span class="comment">// (キャストでもそうです。なぜなら型が C に関係づけられているからです)</span> +</pre> +</td></tr></tbody></table> +<p>この不変性は、<code>nsCOMPtr<nsISupports></code> では、緩められます。<code>nsISupports*</code>(あるいは<code>void*</code>)のように、人々は一般的に<code>nsCOMPtr<nsISupports></code> を「任意の [XP]COM インタフェース」とみなして使います。もし実際の型を気にしないようなオブジェクトに対して、<code>nsCOMPtr</code> が [XP]COM として正しい <code>nsISupports</code> に <code>QueryInterface</code> することを強制するのであれば、煩わしいかもしれません。 +</p> +<h5 id="NULL_.E9.96.93.E6.8E.A5.E5.8F.82.E7.85.A7.E3.81.AE.E4.BF.9D.E8.AD.B7.E6.89.8B.E6.AE.B5" name="NULL_.E9.96.93.E6.8E.A5.E5.8F.82.E7.85.A7.E3.81.AE.E4.BF.9D.E8.AD.B7.E6.89.8B.E6.AE.B5"> <code>NULL</code> 間接参照の保護手段 </h5> +<p>もし中が空の時に間接参照しようとすると、<code>nsCOMPtr</code> は、実行時にアサートします。例えば、 +</p> +<table> +<tbody><tr> +<td> +<pre class="eval">nsCOMPtr<nsIFoo> foo; + <span class="comment">// 注: デフォルトでは、|0| に初期化されます。</span> + +<span class="warning">foo->DoSomething();</span> + <span class="comment">// NS_PRECONDITION: "You can't dereference a NULL nsCOMPtr with operator->()"</span> +</pre> +</td></tr></tbody></table> +<p>同様の事前条件が <code>operator*</code> のために、介在します。 +</p> +<h5 id=".E5.8F.82.E7.85.A7.E3.82.AB.E3.82.A6.E3.83.B3.E3.83.88.E3.81.AE.E4.BF.9D.E8.AD.B7.E6.89.8B.E6.AE.B5" name=".E5.8F.82.E7.85.A7.E3.82.AB.E3.82.A6.E3.83.B3.E3.83.88.E3.81.AE.E4.BF.9D.E8.AD.B7.E6.89.8B.E6.AE.B5"> 参照カウントの保護手段 </h5> +<p><code>nsCOMPtr</code> から元の生のポインタを取り出すすべての操作に対して、安全な特徴を実装するための C の別のトリックを使います。返ってきたポインタに対して、<code>AddRef</code>、<code>Release</code>、<code>delete</code>を実行することができません。 +</p> +<table> +<tbody><tr> +<td> +<pre class="eval">nsCOMPtr<nsIFoo> foo = ...; + +<span class="warning">foo->AddRef();</span> <span class="comment">// エラー: |AddRef| はプライベートです。</span> +<span class="warning">delete foo.get();</span> <span class="comment">// エラー: |operator delete| はプライベートです。</span> +<span class="warning">NS_RELEASE(foo);</span> <span class="comment">// エラー: |Release| はプライベートです。</span> +</pre> +</td></tr></tbody></table> +<p>もちろん、<code>nsCOMPtr</code> によって提供される安全性に関する最も重要な特徴は、それが適切な時期に自動的に <code>AddRef</code> と <code>Release</code> を実行することです。 +</p> +<h4 id=".E3.82.AD.E3.83.A3.E3.82.B9.E3.83.88" name=".E3.82.AD.E3.83.A3.E3.82.B9.E3.83.88"> キャスト </h4> +<p><code>nsCOMPtr</code> では、旧式の C のキャストを使わないでください。旧式のキャストは、たとえそれが正しくないとしても、コンパイルできることが保障されています。旧式のキャストは、もし変換が定義されていないのであれば、<code>reinterpret_cast</code> と同じものにしてしまいます。そのようなキャストは、<code>nsCOMPtr</code> の機構を容易にバイパスしてしまい、リークの発生、型の不一致、その他の不幸な出来事を招きます。 +</p> +<table> +<tbody><tr> +<td> +<pre class="eval"><span class="comment">// 旧式の C のキャストが |nsCOMPtr| 機構をバイパスし、リークを発生させる...</span> + +nsresult rv; +nsCOMPtr<nsIFoo> foo = ...; + +<span class="comment">// ...</span> +rv = GetFoo( <span class="warning">(nsIFoo**)&foo</span> ); +rv = GetFoo( <span class="warning">&(nsIFoo*)foo</span> ); + <span class="comment">// もちろん、これらはコンパイルできます。でもリークしてしまいます。</span> +</pre> +</td></tr></tbody></table> +<p>これを防ぐのを助けるために、上記のように <code>operator&</code> を <code>private</code> にすることにより、最初の形を不法とすることを試みます。<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=59414" title="FIXED: misuse of nsCOMPtr::operator&">バグ 59414</a> を参照してまださい。 +</p> +<h4 id=".E5.AE.9F.E8.A3.85.E3.81.AE.E8.A9.B3.E7.B4.B0.E3.81.A8.E3.83.87.E3.83.90.E3.83.83.E3.82.B0.E6.A9.9F.E6.A7.8B" name=".E5.AE.9F.E8.A3.85.E3.81.AE.E8.A9.B3.E7.B4.B0.E3.81.A8.E3.83.87.E3.83.90.E3.83.83.E3.82.B0.E6.A9.9F.E6.A7.8B"> 実装の詳細とデバッグ機構 </h4> +<p><code>nsCOMPtr</code> はクラスですが、virtual なメソッドを持っていません。つまり、vtable または vptr を持っていません。キーとなるいくつかのルーチンが共通の非テンプレートの基底クラスに分解されるので、実際の元のポインタは、<code>nsISupports*</code> として保存されます (ただしデバッグビルドで <code>NSCAP_FEATURE_DEBUG_PTR_TYPES</code> がオンになっている場合は除きます)。それは、これらの分解されたルーチンのために、<code>nsCOMPtr</code> のユーザが XPCOM ライブラリとリンクしなければいけないからです。 +</p><p><code>NSCAP_FEATURE_DEBUG_PTR_TYPES</code> がオンになっている時、<code>nsISupports*</code> 型の変数に元のポインタを保持する代わりに、<code>nsCOMPtr</code> は、元の型に適合するポインタに保持します。これにより、ソースレベルのデバッガがより簡単にポインタを「追跡」できるようになります。しかしながら、基底クラスに分解されるルーチンは、今やテンプレート特有のインラインコードとなります。分解される基底クラスはありません。これは、すべてのアプリケーションが <code>NSCAP_FEATURE_DEBUG_PTR_TYPES</code> について同じ設定でコンパイルされなければならないことを意味します。そうでないと、いくつかの部分では基底クラスを期待し、他の部分ではそうでないことを期待することになります。アプリケーションは、リンクできないでしょう。 +</p> +<h4 id=".E3.83.A6.E3.83.8B.E3.83.83.E3.83.88.E3.83.86.E3.82.B9.E3.83.88" name=".E3.83.A6.E3.83.8B.E3.83.83.E3.83.88.E3.83.86.E3.82.B9.E3.83.88"> ユニットテスト </h4> +<p><code>nsCOMPtr</code>のためのユニットテストは、このファイルにあります。 +</p> +<ul><li> <code><a href="https://dxr.mozilla.org/mozilla-central/source//xpcom/tests/TestCOMPtr.cpp" rel="custom">/xpcom/tests/TestCOMPtr.cpp</a></code> +</li></ul> +<h3 id=".E5.88.9D.E6.9C.9F.E5.8C.96.E3.81.A8.E4.BB.A3.E5.85.A5" name=".E5.88.9D.E6.9C.9F.E5.8C.96.E3.81.A8.E4.BB.A3.E5.85.A5"> 初期化と代入 </h3> +<h4 id=".E7.B5.84.E3.81.BF.E8.BE.BC.E3.81.BF.E5.BD.A2.E5.BC.8F" name=".E7.B5.84.E3.81.BF.E8.BE.BC.E3.81.BF.E5.BD.A2.E5.BC.8F"> 組み込み形式 </h4> +<p><code>nsCOMPtr</code> への代入や初期化は、簡単に理解できます。<code>nsCOMPtr</code> は、その古い値がもしあれば <code>Release</code> し、そして新しい値を代入し、<code>AddRef</code> を呼び出し、および/または、あなたが直接「注釈」することによって <code>dont_AddRef</code> のような指示子で代入する <code>QueryInterface</code> を呼び出します。このセクションでは、それぞれ起こりうる場合を記述します。ただし、指示子については、より簡潔に以下の表で記述しています。 +</p><p>あなたは、<code>nsCOMPtr</code> を以下のものから構築するか、以下のものから代入することができます。 +</p> +<ul><li> 値 <code>0</code> +</li><li> 同じ型の他の <code>nsCOMPtr</code> +</li><li> 同じ型の生の [XP]COM インタフェースポインタ +</li><li> 同じ型の生の [XP]COM インタフェースポインタで、かつ <a href="#nsCOMPtr.3CT.3E_.3D_T.2A.2CnsCOMPtr.3CT.3E_.3D_dont_QueryInterface.28_T.2A_.29"><code>dont_QueryInterface</code></a> 指示子により注釈されたもの。 +</li><li> 同じ型の生の [XP]COM インタフェースポインタで、かつ <a href="#nsCOMPtr.3CT.3E_.3D_dont_AddRef.28_T.2A_.29.2CnsCOMPtr.3CT.3E_.3D_getter_AddRefs.28_T.2A_.29"><code>dont_AddRef</code></a> 指示子や同様のものにより注釈されたもの。 +</li><li> 任意の型の任意のインタフェースポインタ (<code>nsCOMPtr</code> でも、生の [XP]COM インタフェースポインタでも) で、かつ <a href="#nsCOMPtr.3CT.3E_.3D_do_QueryInterface.28_nsISupports.2A_.29.2CnsCOMPtr.3CT.3E_.3D_do_QueryInterface.28_nsISupports.2A.2C_nsresult.2A_.29"><code>do_QueryInterface</code></a> 指示子により注釈されたもの。 +</li><li> <a href="#nsCOMPtr.3CT.3E_.3D_do_QueryReferent.28_nsIWeakReference.2A_.29.2CnsCOMPtr.3CT.3E_.3D_do_QueryReferent.28_nsIWeakReference.2A.2C_nsresult.2A_.29"><code>do_QueryReferent</code></a> 指示子 +</li></ul> +<p>最初の三つは、単純で明らかです。4 番目のもの (<code>dont_QueryInterface</code> 指示子の適用) は、同じ型の生の [XP]COM インタフェースポインタの代入と同じことです。残りの指示子は、特殊な状況において、いくつかの付加的な制御を提供するものです。さらに、<code>nsCOMPtr</code> を初期値なしで構築することができ、その場合は、<code>0</code> で初期化されます。ちょうどプリミティブなポインタのように、値が<code>0</code>の<code>nsCOMPtr</code> は、どのオブジェクトも指しません。そして、<code>if (foo)</code> や <code>if (!foo)</code> のように式をテストすることができます。 +</p><p>上述した指示は、この表によりもっとはっきりするでしょう。 +</p> +<table> +<caption> 表 1. <code>nsCOMPtr</code>に代入するオプション +</caption> +<tbody><tr> +<td> +</td><td> QueryInterface しない +</td><td> QueryInterface する +</td></tr> +<tr> +<td> <code>AddRef</code> する +</td><td> +<p><a href="#nsCOMPtr.3CT.3E_.3D_T.2A.2CnsCOMPtr.3CT.3E_.3D_dont_QueryInterface.28_T.2A_.29">T*,<br><code>dont_QueryInterface(T*)</code></a> +</p> +</td><td> +<p><a href="#nsCOMPtr.3CT.3E_.3D_do_QueryInterface.28_nsISupports.2A_.29.2CnsCOMPtr.3CT.3E_.3D_do_QueryInterface.28_nsISupports.2A.2C_nsresult.2A_.29"><code>do_QueryInterface(nsISupports*)</code>, <br><code>do_QueryInterface(nsISupports*, nsresult*)</code></a> <a href="#nsCOMPtr.3CT.3E_.3D_do_QueryReferent.28_nsIWeakReference.2A_.29.2CnsCOMPtr.3CT.3E_.3D_do_QueryReferent.28_nsIWeakReference.2A.2C_nsresult.2A_.29"><code>do_QueryReferent(nsIWeakReference*)</code>, <br><code>do_QueryReferent(nsIWeakReference*, nsresult*)</code></a> +</p> +</td></tr> +<tr> +<td> <code>AddRef</code> しない +</td><td> +<p><a href="#nsCOMPtr.3CT.3E_.3D_dont_AddRef.28_T.2A_.29.2CnsCOMPtr.3CT.3E_.3D_getter_AddRefs.28_T.2A_.29"><code>dont_AddRef(T*)</code>,<br><code>getter_AddRefs(T*)</code></a> +</p> +</td><td> +<p><a href="#nsCOMPtr.3CT.3E_.3D_.2F.2A_call_QueryInterface_but_don.27t_AddRef_.2A.2F">n/a</a> +</p> +</td></tr></tbody></table> +<p>例えば、<code>nsCOMPtr</code> への代入において、(なんらかの理由ですでに <code>AddRef</code> を実行したために)代入するポインタに対して <code>AddRef</code> を実行したくない場合、「<code>AddRefしない</code>」と「QueryInterface しない」の交差する所にある <code>dont_AddRef(T*)</code> を使うのは一つの可能性です。以下に、<code>dont_AddRef</code>を使い、様々な位置にそれらの「注釈」が表れるサンプルを示します。 +</p> +<table> +<tbody><tr> +<td> +<pre class="eval"><span class="comment">// |nsCOMPtr| への代入を管理する...</span> + + <span class="comment">// コンストラクタにおいて...</span> +nsCOMPtr<nsIFoo> foo1( <span class="notice">dont_AddRef(</span>rawFoo1Ptr<span class="notice">)</span> ); +nsCOMPtr<nsIFoo> foo2 = <span class="notice">dont_AddRef(</span>rawFoo2Ptr<span class="notice">)</span><span class="nowiki">; + </span><span class="comment">// (直接の初期化と呼ばれる) 関数の形式と (コピーの初期化と呼ばれる)</span> + <span class="comment">// コンストラクタの代入の形式は、微妙に異なる意味を</span> + <span class="comment">// 持つことに注意してください。直接の初期化の方が好ましい。</span> + +nsCOMPtr<nsIFoo> foo3; + + <span class="comment">// 通常の代入において...</span> +foo3 = <span class="notice">dont_AddRef(</span>rawFoo3Ptr<span class="notice">)</span><span class="nowiki">; + + </span><span class="comment">// 表で記述されている注釈をコンストラクタと</span> + <span class="comment">// 単純で古いタイプの代入に適用しています。</span> +</pre> +</td></tr></tbody></table> +<p>表に示されたどの注釈も <code>dont_AddRef()</code> を使って示されたすべての場所に出現可能です。続くセクションでそれぞれの可能性を記述します。 +</p> +<h5 id="nsCOMPtr.3CT.3E_.3D_T.2A.2CnsCOMPtr.3CT.3E_.3D_dont_QueryInterface.28_T.2A_.29" name="nsCOMPtr.3CT.3E_.3D_T.2A.2CnsCOMPtr.3CT.3E_.3D_dont_QueryInterface.28_T.2A_.29"> <code>nsCOMPtr<T> = T*</code>,<br><code>nsCOMPtr<T> = dont_QueryInterface( T* )</code> </h5> +<p>表で <code>T*</code> として示されるデフォルトの振舞いでは、新しい値に対して、<code>AddRef</code> を実行します。しかし、それに対して、<code>QueryInterface</code> は実行しません。「注釈」がない時に何が起きるかという例を示します。例えば、 +</p> +<table> +<tbody><tr> +<td> +<pre class="eval">nsCOMPtr<nsIFoo> foo( aFooPtr ); <span class="comment">// もしくは</span> +foo = aFooPtr; + <span class="comment">// ...|AddRef| は呼び出しますが、|QueryInterface| は呼び出しません。|</span> + + <span class="comment">// 同じものをより明示的に表すと...</span> +nsCOMPtr<nsIFoo> foo( <span class="notice">dont_QueryInterface(</span>aFooPtr<span class="notice">)</span> ); <span class="comment">// もしくは</span> +foo = <span class="notice">dont_QueryInterface(</span>aFooPtr<span class="notice">)</span><span class="nowiki">; + </span> +</pre> +</td></tr></tbody></table> +<p>この形式を使うことにより、あなたが代入しているポインタがすでに、<code>nsCOMPtr</code> の元の型、この場合は、<code>nsIFoo</code> に適合している、[XP]COMとして正しいインタフェースへのポインタであることを約束していることになります。 +</p> +<h5 id="nsCOMPtr.3CT.3E_.3D_do_QueryInterface.28_nsISupports.2A_.29.2CnsCOMPtr.3CT.3E_.3D_do_QueryInterface.28_nsISupports.2A.2C_nsresult.2A_.29" name="nsCOMPtr.3CT.3E_.3D_do_QueryInterface.28_nsISupports.2A_.29.2CnsCOMPtr.3CT.3E_.3D_do_QueryInterface.28_nsISupports.2A.2C_nsresult.2A_.29"> <code>nsCOMPtr<T> = do_QueryInterface( nsISupports* )</code>,<br><code>nsCOMPtr<T> = do_QueryInterface( nsISupports*, nsresult* )</code> </h5> +<p>もし、上記の約束を果たせない時は、<code>nsCOMPtr</code> に対して、代入において <code>QueryInterface</code> を呼び出す必要があると、「注釈」をすることができます。例えば、 +</p> +<table> +<tbody><tr> +<td> +<pre class="eval">nsCOMPtr<nsIFoo> foo( <span class="notice">do_QueryInterface(</span>aBarPtr<span class="notice">)</span> ); <span class="comment">// もしくは</span> +foo = <span class="notice">do_QueryInterface(</span>aBarPtr<span class="notice">)</span><span class="nowiki">; + </span><span class="comment">// ...|QueryInterface| が呼ばれる _でしょう_。(その結果 |AddRef| も呼ばれます)</span> + + <span class="comment">// もちろん、|QueryInterface| を呼んでいるので、</span> + <span class="comment">// エラー結果も必要になるでしょう...</span> +nsresult rv; +nsCOMPtr<nsIFoo> foo( <span class="notice">do_QueryInterface(</span>aBarPtr<span class="notice">, &rv)</span> ); <span class="comment">// もしくは</span> +foo = <span class="notice">do_QueryInterface(</span>aBarPtr<span class="notice">, &rv)</span><span class="nowiki">; + </span> +</pre> +</td></tr></tbody></table> +<h5 id="nsCOMPtr.3CT.3E_.3D_dont_AddRef.28_T.2A_.29.2CnsCOMPtr.3CT.3E_.3D_getter_AddRefs.28_T.2A_.29" name="nsCOMPtr.3CT.3E_.3D_dont_AddRef.28_T.2A_.29.2CnsCOMPtr.3CT.3E_.3D_getter_AddRefs.28_T.2A_.29"> <code>nsCOMPtr<T> = dont_AddRef( T* )</code>,<br><code>nsCOMPtr<T> = getter_AddRefs( T* )</code> </h5> +<p>時々、すでに <code>AddRef</code> が実行されたポインタをたまたま持っていて、それを <code>nsCOMPtr</code> に代入したい場合があるでしょう。これは、しばしば、(<code>nsresult</code> を結果とするのではなく) <code>AddRef</code> が実行されたポインタを結果として返す getter を使った時に起きます。あるいは、効率性のための変形により起きる場合もあります。<code>dont_AddRef</code> は、このような場合の完璧な治療法です。 +</p> +<table> +<tbody><tr> +<td> +<pre class="eval">nsIFoo* temp; +nsresult rv = GetFoo(&temp); +nsCOMPtr<nsIFoo> foo( <span class="notice">dont_AddRef(</span>temp<span class="notice">)</span> ); + <span class="comment">// |temp| はすでに |AddRef| を実行済ですが、我々はこれを</span> + <span class="comment">// |nsCOMPtr| で管理しようとしています。.</span> + +nsCOMPtr<nsIFoo> foo( <span class="notice">getter_AddRefs(</span>CreateAFoo()<span class="notice">)</span> ); + <span class="comment">// |getter_AddRefs| は |dont_AddRef| の同意語です。</span> + <span class="comment">// これは、|AddRef| が実行されたポインタを返す関数に適用する時に分かりやすくするものです。</span> + +nsCOMPtr<nsIFoo> foo( <span class="notice">dont_AddRef(</span>CreateAFoo()<span class="notice">)</span> ); + <span class="comment">// あるいは、あなたはそれが好きではないかもしれません...</span> +</pre> +</td></tr></tbody></table> +<h5 id="nsCOMPtr.3CT.3E_.3D_.2F.2A_QueryInterface_.E3.82.92.E5.91.BC.E3.81.B3.E5.87.BA.E3.81.97.E3.81.BE.E3.81.99.E3.81.8C.E3.80.81AddRef_.E3.81.AF.E5.91.BC.E3.81.B3.E5.87.BA.E3.81.97.E3.81.BE.E3.81.9B.E3.82.93.E3.80.82_.2A.2F" name="nsCOMPtr.3CT.3E_.3D_.2F.2A_QueryInterface_.E3.82.92.E5.91.BC.E3.81.B3.E5.87.BA.E3.81.97.E3.81.BE.E3.81.99.E3.81.8C.E3.80.81AddRef_.E3.81.AF.E5.91.BC.E3.81.B3.E5.87.BA.E3.81.97.E3.81.BE.E3.81.9B.E3.82.93.E3.80.82_.2A.2F"> <code>nsCOMPtr<T> = </code>/* <code>QueryInterface</code> を呼び出しますが、<code>AddRef</code> は呼び出しません。 */ </h5> +<p>表のこの象限が「n/a (not applicable)」とマークされているのに気づくでしょう。「<code>QueryInterface</code> を呼び出すが、<code>AddRef</code> を行わないこと」を意味する明示的な指令はありません。このオプションは、間違った型のオブジェクトを返す getter を呼び出す状況に対応します。すでに <code>AddRef</code> を実行したオブジェクトを持っているので、もう AddRef を実行したくないが、違うインタフェースを得る必要がある場合です。それはできません。<code>QueryInterface</code> は、常に <code>AddRef</code> をその結果に対して実行します。そして、正しい型を得るための <code>QueryInterface</code> の呼び出しの代用品は存在しません。解決するには、2 段階のプロセスを実行します。 +</p> +<table> +<tbody><tr> +<td> +<pre class="eval"><span class="comment">// ...</span> + + <span class="comment">// getter は (間違った型の) すでに |AddRef| を実行したオブジェクトを返します...</span> +nsCOMPtr<nsIBar> bar( <span class="notice">getter_AddRefs(</span>CreateBar()<span class="notice">)</span> ); + <span class="comment">// ...(このオブジェクトに対して) 正しい型を問い合わせる必要があります。</span> +nsCOMPtr<nsIFoo> foo( <span class="notice">do_QueryInterface(</span>bar<span class="notice">)</span> ); +</pre> +</td></tr></tbody></table> +<p>この場合において、人々が陥る不運なワナは、getter 関数が結果を <code>AddRef</code> していることを忘れることです。こんな感じのコードをタイプしてしまいます: +</p> +<table> +<tbody><tr> +<td> +<pre class="eval">nsCOMPtr<nsIFoo> foo( <span class="warning">do_QueryInterface(CreateBar())</span> ); + <span class="comment">// おっと! |CreateBar| によって返ってくるインタフェースがリークしてしまいます。</span> + <span class="comment">// この場合、あなたは上に示した二つのステップの解決法で処理する_必要_があります。</span> + + <span class="comment">// ありそうもないですか? こんな感じの形で見ることはあるでしょう。</span> +nsCOMPtr<nsIFoo> foo( <span class="warning">do_QueryInterface(aList->ElementAt(i))</span> ); + <span class="comment">// すべての良い getter のように、|ElementAt| は、</span> + <span class="comment">// インタフェースからの必要性に応じて問い合わせを行なった後、</span> + <span class="comment">// 破棄されるかも知れない結果を |AddRef| します。</span> +</pre> +</td></tr></tbody></table> +<p>Bugzilla <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=8221" title="FIXED: MLK - Mem Leak's found with the scc wonder query - nsCOMPtr = do_QueryInterface..">バグ 8221</a> は、この特定のリークの発見と修正に限定されたものです。 +</p> +<h4 id="nsCOMPtr_.E3.83.98.E3.83.AB.E3.83.91.E3.83.BC" name="nsCOMPtr_.E3.83.98.E3.83.AB.E3.83.91.E3.83.BC"> <code>nsCOMPtr</code> ヘルパー </h4> +<h5 id="nsCOMPtr.3CT.3E_.3D_do_QueryReferent.28_nsIWeakReference.2A_.29.2CnsCOMPtr.3CT.3E_.3D_do_QueryReferent.28_nsIWeakReference.2A.2C_nsresult.2A_.29" name="nsCOMPtr.3CT.3E_.3D_do_QueryReferent.28_nsIWeakReference.2A_.29.2CnsCOMPtr.3CT.3E_.3D_do_QueryReferent.28_nsIWeakReference.2A.2C_nsresult.2A_.29"> <code>nsCOMPtr<T> = do_QueryReferent( nsIWeakReference* )</code>,<br><code>nsCOMPtr<T> = do_QueryReferent( nsIWeakReference*, nsresult* )</code> </h5> +<p><a href="ja/Weak_reference"><code>nsIWeakReference</code></a> に基づく弱い参照を容易にする <code>do_QueryReferent</code> というのがあります。<code>nsIWeakReference</code> は、他のオブジェクトのプロキシとして振舞う [XP]COM オブジェクトです。<code>nsIWeakReference</code> と (上記の) 他のオブジェクトは、特別な関係にあります。それらは、お互いのことを知っています。しかし、どちらももう一方への所有する参照を保持していません。二つのオブジェクトは、もう一方へのダングリングポインタを持たないことを保障するように協調しています。<code>nsIWeakReference</code> オブジェクトにおいて所有する参照を保持することにより、この他のオブジェクトを必要な時に得ることができ、しかし、それ (他のオブジェクト) が生きていなくてもよいのです。そのオブジェクトを得るためには、<code>nsIWeakReference</code> オブジェクトに、あなたの代わりに <code>QueryInterface</code> するように依頼します。もしオブジェクトがまだ存在しており、要求されたインタフェースをサポートしているのであれば、あなたは (できれば、一時的に) それに対する所有する参照を持つことができます。 +</p> +<table> +<tbody><tr> +<td> +<pre class="eval">nsIWeakReference* weakPtr = ...; + +weakPtr->QueryReferent( +</pre> +</td></tr></tbody></table> +<h3 id="T.2A_.E3.81.A8.E3.81.97.E3.81.A6_nsCOMPtr.3CT.3E_.E3.82.92.E4.BD.BF.E3.81.86" name="T.2A_.E3.81.A8.E3.81.97.E3.81.A6_nsCOMPtr.3CT.3E_.E3.82.92.E4.BD.BF.E3.81.86"> <code>T*</code> として <code>nsCOMPtr<T></code> を使う</h3> +<h4 id="nsCOMPtr.E3.82.92.E3.83.9D.E3.82.A4.E3.83.B3.E3.82.BF.E3.81.A8.E3.81.97.E3.81.A6.E4.BD.BF.E3.81.86" name="nsCOMPtr.E3.82.92.E3.83.9D.E3.82.A4.E3.83.B3.E3.82.BF.E3.81.A8.E3.81.97.E3.81.A6.E4.BD.BF.E3.81.86"> <code>nsCOMPtr</code>をポインタとして使う </h4> +<h4 id=".E3.80.8C.E5.85.A5.E5.8A.9B.E3.80.8D.E3.83.91.E3.83.A9.E3.83.A1.E3.82.BF" name=".E3.80.8C.E5.85.A5.E5.8A.9B.E3.80.8D.E3.83.91.E3.83.A9.E3.83.A1.E3.82.BF"> 「入力」パラメタ </h4> +<h4 id=".E3.80.8C.E5.87.BA.E5.8A.9B.E3.80.8D.E3.83.91.E3.83.A9.E3.83.A1.E3.82.BF_getter_AddRefs" name=".E3.80.8C.E5.87.BA.E5.8A.9B.E3.80.8D.E3.83.91.E3.83.A9.E3.83.A1.E3.82.BF:_getter_AddRefs"> 「出力」パラメタ: <code>getter_AddRefs</code> </h4> +<p><code>nsCOMPtr</code> への代入は、とても理解しやすいです。<code>nsCOMPtr</code> は、古い値がもしあれば、それを <code>Release</code> します。そして、代入した新しい値を <code>AddRef</code> し、および/または上述した指令に示された<code>QueryInterface</code> を呼び出します。これらの規則は、<code>nsCOMPtr</code> として宣言されたパラメタや関数の復帰値のコピーにおいて起こる「代入」でも同じく適用されます。もし <code>nsCOMPtr</code> を生の [XP]COM インタフェースポインタの実用的な代用品としたいのであれば、しかしながら、「出力」パラメタの問題に対処する必要があります。多くの [XP]COM 関数は、結果のインタフェースポインタをパラメタを通じて返します。例えば、 +</p> +<table> +<tbody><tr> +<td> +<pre class="eval"><span class="comment">// Getter は、インタフェースポインタを「出力」パラメタを通じて返すことができます。...</span> + +nsresult GetFoo( nsIFoo** ); <span class="comment">// 標準的 getter</span> +nsresult GetFoo2( nsIFoo*& ); <span class="comment">// 非標準的 getter</span> +nsresult GetSomething( void** ); <span class="comment">// 「型無し」の getter</span> + <span class="comment">// 注: |QueryInterface| は、「型無し」の getter の例です。</span> +</pre> +</td></tr></tbody></table> +<p>我々は、「出力」パラメタを使うルーチンへポインタや参照によって <code>nsCOMPtr</code> を渡せなければいけません。問題は、getter 内部には、<code>nsCOMPtr</code> に対する情報がないことです。それは、生の [XP]COM インタフェースポインタへのポインタ (または参照) を得ていると考えます。<code>nsCOMPtr</code> のスマートな代入演算子は、呼ばれません。古い値があれば、リークしてしまいます。 +</p><p>ここで、<code>getter_AddRefs( nsCOMPtr& )</code>が役に立ちます。<code>getter_AddRefs</code> は、古い値があれば <code>Release</code> し、それをクリアします。そして、それに対するポインタを返し、getter は <code>nsCOMPtr</code>に<code>AddRef</code> を実行した新しい値を設定します。我々は、これらの状況で、生の [XP]COM インタフェースポインタに適用していた <code>&</code> を置き換えるものとして、<code>getter_AddRef</code> を使用します。<code>getter_AddRefs</code> は、通常 <code>nsCOMPtr</code> のコンストラクタと代入演算子から得ていた魔法を詰めこんだものです。 +</p> +<table> +<tbody><tr> +<td> +<pre class="eval"><span class="comment">// 生の [XP]COM インタフェースポインタ...</span> + +nsIFoo<span class="notice"><span class="nowiki">*</span></span> foo; + +GetFoo(<span class="notice">&</span>foo); +GetFoo2(foo); +GetSomething(<span class="notice">(void**)&</span>foo); +</pre> +</td><td> +<pre class="eval"><span class="comment">// |nsCOMPtr|...</span> + +<span class="notice">nsCOMPtr<</span>nsIFoo<span class="notice">></span> foo; + +GetFoo(<span class="notice">getter_AddRefs(</span>foo<span class="notice">)</span>); +GetFoo2(<span class="notice"><span class="nowiki">*getter_AddRefs(</span></span>foo<span class="notice">)</span>); +GetSomething(<span class="notice">getter_AddRefs(</span>foo<span class="notice">)</span>); +</pre> +</td></tr></tbody></table> +<p>これを実現するのに、なぜ単に <code>operator&</code> をオーバーロードしないのでしょうか? いくつかの理由: 他の状況では、<code>nsCOMPtr</code> のアドレスを取るのは、不便なことになります。「<code>getter_AddRefs</code>」という名前は、getter としてある一定の振舞いを強制します。そして、かつては、他の可能性がありました (あなたがまさに学ぼうとしているように)。 +</p><p>パラメタを通じて復帰値を返しますが、<code>AddRef</code> を実行していない getter のために、<code>getter_doesnt_AddRef( nsCOMPtr& )</code> というのは、ありますか? いいえ、ありません。かつてありましたが、それは 3 つの理由でなくなりました: +</p> +<ul><li> getter がパラメタを通じて <code>AddRef</code> を実行していないインタフェースポインタを返すのは、[XP]COM の規則に反しています。(もしそれを見つけたら、バグ報告をしてください。) +</li><li> <code>getter_doesnt_AddRef</code> は、<code>nsCOMPtr</code> を生の [XP]COM インタフェースポインタよりも大きく、または遅くしてしまう複雑な波及効果があります。 +</li><li> とりあえず、そのような getter を呼んで、一時的に<code>nsCOMPtr</code>に結果を入れることもできます。例えば、 +</li></ul> +<table> +<tbody><tr> +<td> +<pre class="eval"><span class="comment">// その結果に対して、|AddRef| を (違法に) 呼び出さない getter を呼び出します...</span> + +nsIFoo* temp; +nsresult rv = <span class="warning">GetFoo_WithoutAddRef(</span>&temp<span class="warning">)</span><span class="nowiki">; + </span><span class="comment">// 自分への注: |GetFoo_WithoutAddRef| をバグとして報告しなければならない。</span> + <span class="comment">// すべての getter は、 |AddRef| しなければならない。</span> +nsCOMPtr<nsIFoo> foo = temp; +</pre> +</td></tr></tbody></table> +<h4 id=".E3.80.8C.E5.85.A5.E5.87.BA.E5.8A.9B.E3.80.8D.E3.83.91.E3.83.A9.E3.83.A1.E3.82.BF" name=".E3.80.8C.E5.85.A5.E5.87.BA.E5.8A.9B.E3.80.8D.E3.83.91.E3.83.A9.E3.83.A1.E3.82.BF"> 「入出力」パラメタ </h4> +<p>「入力/出力」パラメタについては、どうなんでしょう? +</p> +<h3 id=".E5.8A.B9.E7.8E.87.E6.80.A7.E3.81.A8.E6.AD.A3.E7.A2.BA.E6.80.A7" name=".E5.8A.B9.E7.8E.87.E6.80.A7.E3.81.A8.E6.AD.A3.E7.A2.BA.E6.80.A7"> 効率性と正確性 </h3> +<h4 id="nsCOMPtr.E3.81.AE.E3.82.B3.E3.82.B9.E3.83.88" name="nsCOMPtr.E3.81.AE.E3.82.B3.E3.82.B9.E3.83.88"> <code>nsCOMPtr</code>のコスト </h4> +<p><code>nsCOMPtr</code> は、生の [XP]COM インタフェースポインタに対する実用的な置き換えとなるべく調整されています。所有する参照として使うのであれば、どの場所においてもです。<code>nsCOMPtr</code>s のパフォーマンスについては一般的に、スペース的には少し効率が<em>よく</em>、時間的には、ごくわずかに効率が悪いです。パフォーマンスに関することにより、<code>nsCOMPtr</code> を使わないのは良くありません。このセクションを通じて提示するパターンが<code>nsCOMPtr</code> からより多くのものを引き出すのを手伝ってくれるでしょう。 +</p> +<h5 id=".E3.82.B9.E3.83.9A.E3.83.BC.E3.82.B9" name=".E3.82.B9.E3.83.9A.E3.83.BC.E3.82.B9"> スペース </h5> +<p>一般的に、<code>nsCOMPtr</code>は、生の [XP]COM ポインタを使うよりもスペース的には、効率がよくなる<i>可能性</i>があります。これは主にそのデストラクタの分解とより複雑なコンストラクタと代入演算子のためです。このセクションの最適化の tips に従うことで、生のポインタで使用するのよりもオブジェクトの生成するバイトがより少ないコードを書くことができるでしょう。これらの忠告に従わないとしても、<code>nsCOMPtr</code> のコードは、依然として、より小さくなり、あるいは最悪でも生のポインタ版よりもごくわずか増えるだけです。詳細については、<a class="external" href="http://www.mozilla.org/projects/xpcom/nsCOMPtr/bloat.html">Code Bloat [長文、要約が最初にあります</a>] を見てください。もっとも、ここでは、そのドキュメントからの推奨事項を繰り返しています。 +</p> +<h5 id=".E6.99.82.E9.96.93" name=".E6.99.82.E9.96.93"> 時間 </h5> +<p><span class="editor-note"><span class="nowiki">[[もっと時間パフォーマンスの測定が必要です。]]</span></span> +</p><p>二つ以上のサブルーチン、すなわち <code>AddRef</code>、<code>Release</code>、<code>QueryInterface</code> が必要な場所では、いくつかの <code>nsCOMPtr</code>ル ーチンが分解され、そのためサブルーチンを呼び出しと対応する付加的な時間を必要とします。この時間は、特に <code>QueryInterface</code> により行われる作業や <code>Release</code> により行われるかもしれない作業にもかかわらず、極わずかです。 +</p><p>その他のすべての場合、<code>nsCOMPtr</code> は手で行われる作業をするだけです。<code>nsCOMPtr</code> が使用される作業の大部分は、<code>operator-></code> での間接参照であり、原始的なポインタが行うものと同じです。この操作は、すべてのプラットフォームで、生の [XP]COM インタフェースポインタでの操作とちょうど同じコードを作り出し、そして同じ時間を消費します。デストラクタは、クライアントコードが生の [XP]COM インタフェースポインタに対して、<code>Release</code> を呼び出すのに対応しますが、処理が分解されているため、サブルーチンを呼び出すための余分な時間が必要となります。もっとも、これは、<code>delete</code> を実行するかもしれない <code>Release</code> の呼び出しと参照する方のデストラクタの両方の場合においてすでに存在するコストに対して、バランスが取れています。すべての <code>nsCOMPtr</code> のコンストラクタと代入演算子は、インラインです。単純なコンストラクタ、すなわち問い合わせをしないもの、は、あなたが手で書いたものと同じ作業だけを行います。<code>AddRef</code>、<code>Release</code>、<code>QueryInterface</code> の中の一つ以上の呼び出しを行うすべてのルーチンは、分解されており、そのため、サブルーチン呼び出しの余分なコストを含んでいます。 +</p><p>いくつかのルーチンが分解されているという事実により、余分なサブルーチン呼び出しのオーバーヘッドが生じます。そして、その事実および初期化がバイパスできないという事実によって、生の [XP]COM インタフェースポインタに対して、<code>nsCOMPtr</code> が余分な実行時間のコストを生じるのです。スペースと時間のトレードオフは、<code>nsCOMPtr</code> において見事にバランスが取れています。分解されたルーチンは、膨張に関する測定の直接的な結果です。 +</p> +<h4 id=".E4.BB.A3.E5.85.A5.E3.81.AE.E5.A5.BD.E3.81.BE.E3.81.97.E3.81.84.E6.A7.8B.E7.AF.89" name=".E4.BB.A3.E5.85.A5.E3.81.AE.E5.A5.BD.E3.81.BE.E3.81.97.E3.81.84.E6.A7.8B.E7.AF.89"> 代入の好ましい構築 </h4> +<p>時間においてもスペースにおいても、<code>nsCOMPtr</code> への値の代入の最も効率的な方法は、構築時におけるものです。合理的である限り、代入しながら構築する方が好ましいです。<code>nsCOMPtr</code> のメンバをコンストラクタのメンバ初期化節で初期化すべきです。 +</p> +<table> +<tbody><tr> +<td> +<pre class="eval"><span class="comment">// |nsCOMPtr| のメンバをコンストラクタの</span> +<span class="comment">// メンバ初期化節で初期化する...</span> + +class Bar + { + public: + Bar( nsIFoo* initial_fooPtr ); + <span class="comment">// ...</span> + private: + nsCOMPtr<nsIFoo> mFooPtr; + }; + +Bar::Bar( nsIFoo* initial_fooPtr ) + : <span class="notice">mFooPtr(initial_fooPtr)</span> <span class="comment">// _ここで_ 初期化します</span> + { + <span class="comment">// ここではありません。</span> + } +</pre> +</td></tr></tbody></table> +<p>付け加えておくと、代入の形式を構築の形式に変換する一時的オブジェクトを使う、最適化のパターンがあります。 +</p> +<table> +<tbody><tr> +<td> +<pre class="eval"><span class="comment">// 後に代入が続くデフォルトの</span> +<span class="comment">// 構築は非効率的です...</span> + +nsCOMPtr<nsIFoo> foo; +nsresult rv=GetFoo(getter_AddRefs(foo)); + + + + + +</pre> +</td><td> +<pre class="eval"><span class="comment">// ...構築だけをします。</span> + +<span class="notice">nsIFoo* temp;</span> +nsresult rv=GetFoo(<span class="notice">&temp</span>); +nsCOMPtr<nsIFoo> foo<span class="notice"><span class="nowiki">=dont_AddRef(temp);</span></span> + + <span class="comment">// この「生のポインタ、getter の呼び出し、</span> + <span class="comment">// |dont_AddRef| の代入」パターンを覚えて</span> + <span class="comment">// ください。それは、多くの効率に関する</span> + <span class="comment">// 議論で出てきたものです。</span> +</pre> +</td></tr></tbody></table> +<p>どちらの場合も、あなたは、正当な <code>nsCOMPtr</code> で、その値として <code>GetFoo</code> の結果が設定された <code>foo</code> というオブジェクト、および <code>GetFoo</code> により返された <code>rv</code> という状態を得ます。しかしながら、一時的変数を使う場合は、<code>nsCOMPtr</code> への値の設定をする構築を使っており、(ソース上では、少し複雑になっていますが)、代入に続くデフォルトの構築よりは、効率的になっています。そして、より簡単な例によって、このイベントの過程は理解されるでしょう。 +</p> +<h4 id=".E4.BB.A3.E5.85.A5.E3.81.AB.E3.81.8A.E3.81.91.E3.82.8B.E5.A5.BD.E3.81.BE.E3.81.97.E3.81.84.E7.A0.B4.E5.A3.8A" name=".E4.BB.A3.E5.85.A5.E3.81.AB.E3.81.8A.E3.81.91.E3.82.8B.E5.A5.BD.E3.81.BE.E3.81.97.E3.81.84.E7.A0.B4.E5.A3.8A"> 代入における好ましい破壊 </h4> +<h4 id="QueryInterface_.E3.81.AE.E5.91.BC.E3.81.B3.E5.87.BA.E3.81.97.E3.82.88.E3.82.8A.E3.82.82_do_QueryInterface_.E3.81.AE.E6.96.B9.E3.81.8C.E6.9C.9B.E3.81.BE.E3.81.97.E3.81.84.E3.81.A7.E3.81.99.E3.80.82" name="QueryInterface_.E3.81.AE.E5.91.BC.E3.81.B3.E5.87.BA.E3.81.97.E3.82.88.E3.82.8A.E3.82.82_do_QueryInterface_.E3.81.AE.E6.96.B9.E3.81.8C.E6.9C.9B.E3.81.BE.E3.81.97.E3.81.84.E3.81.A7.E3.81.99.E3.80.82"> <code>QueryInterface</code> の呼び出しよりも <code>do_QueryInterface</code> の方が望ましいです。 </h4> +<h4 id=".E7.B9.B0.E3.82.8A.E8.BF.94.E3.81.97" name=".E7.B9.B0.E3.82.8A.E8.BF.94.E3.81.97"> 繰り返し </h4> +<p>これは、普通のポインタでデータ構造の繰り返しをする時の共通のイディオムです。例えば、 +</p> +<table> +<tbody><tr> +<td> +<pre class="eval"><span class="comment">// [XP]COM オブジェクトでないものに対してポインタで繰り返しを行う...</span> + +Node* p = ...; +while ( p ) + { + <span class="comment">// ...</span> + p = p->next; + } +</pre> +</td></tr></tbody></table> +<p>同様に、このパターンが <code>for</code> ループとしても表現されるのをしばしば見かけます。しかしながら、これを生の [XP]COM インタフェースポインタに対して行うとどうなるか、考えてみてください。 +</p> +<table> +<tbody><tr> +<td> +<pre class="eval"><span class="comment">// 生の [XP]COM インタフェースポインタで繰り返しを行います...</span> + +nsIDOMNode* p = ...; +while ( p ) + { + <span class="comment">// ...</span> + <span class="warning">p->GetNext(&p);</span> + <span class="comment">// 問題です! |p| を |Release| せずに上書きしてしまいました。</span> + } +</pre> +</td></tr></tbody></table> +<p>おっと! <code>p</code> に対して、新しいポインタを設定する前に、<code>Release</code> し損ねてしまいました。みんながこれを多く行うため、これが通常の [XP]COM コードのリークの大きな原因となってしまいました。では、代わりにこうすることはできるのでしょうか ? +</p> +<table> +<tbody><tr> +<td> +<pre class="eval"><span class="comment">// 生の [XP]COM インタフェースポインタで繰り返しを行います...</span> + +nsIDOMNode* p = ...; +while ( p ) + { + <span class="comment">// ...</span> + <span class="warning">NS_RELEASE(p); + p->GetNext(&p);</span> + <span class="comment">// 問題です! ダングリングしているか |NULL| であるポインタの</span> + <span class="comment">// メンバ関数を呼ぼうとしています。</span> + } +</pre> +</td></tr></tbody></table> +<p>残念ながらダメです。<code>Release</code> した後、<code>nsCOMPtr</code> は、ダングリングしている状態になるかもしれません。実は、<code>NS_RELEASE</code> マクロを使うと、<code>p</code> は、<code>GetNext</code> を呼び出すまでは、<code>NULL</code> になるでしょう。 +</p><p>では、同じことを <code>nsCOMPtr</code> で書いてあると想像してみてください。 +</p> +<table> +<tbody><tr> +<td> +<pre class="eval"><span class="comment">// |nsCOMPtr| で繰り返しを行います...</span> + +nsCOMPtr<nsIDOMNode> p = ...; +while ( p ) + { + <span class="comment">// ...</span> + <span class="warning">p->GetNext( getter_AddRefs(p) );</span> + <span class="comment">// 問題です! |NULL| ポインタを通じてメンバ関数を呼び出そうとしました。</span> + } +</pre> +</td></tr></tbody></table> +<p>ここでは、<code>nsCOMPtr</code> の使用は、生の [XP]COM インタフェースポインタの使用とほとんど同じです。<code>getter_AddRefs</code> は、 <code>Release</code> し、そしてそれに代入する前に <code>p</code> をクリアします。すなわち、<code>GetNext</code> が呼ばれる前にそれを行います。これは、<code>GetNext</code> の呼び出しを行う前に、<code>NULL</code> ポインタを通じて、呼び出そうとしてしまうことを意味します。生の [XP]COM インタフェースポインタと違い、<code>nsCOMPtr</code> は、盲目的に <code>NULL</code> ポインタを通じて <code>GetNext</code> を呼び出そうとする代わりに、<code>assert</code> を実行します。 +</p><p>これは問題です。では、解決法は、なんでしょうか。もしこれが生の [XP]COM インタフェースであれば、おそらく一時的変数を導入するでしょう。我々は、<code>nsCOMPtr</code>で同じことをすることができます。 +</p> +<table> +<tbody><tr> +<td> +<pre class="eval"><span class="comment">// 生の [XP]COM インタフェースポインタ</span> +<span class="comment">// での安全な繰り返し...</span> + +nsIDOMNode<span class="notice"><span class="nowiki">*</span></span> p = ...; +while ( p ) + { + <span class="comment">// ...</span> + + <span class="comment">// 一時的変数を導入することで、</span> + <span class="comment">// |p| で足踏みをすることもありません。</span> + nsIDOMNode<span class="notice"><span class="nowiki">*</span></span> temp = p; + temp->GetNext(<span class="notice">&</span>p); + <span class="notice">NS_RELEASE(temp);</span> + } +</pre> +</td><td> +<pre class="eval"><span class="comment">// |nsCOMPtr| での安全な繰り返し...</span> + + +<span class="notice">nsCOMPtr<</span>nsIDOMNode<span class="notice">></span> p = ...; +while ( p ) + { + <span class="comment">// ...</span> + + <span class="comment">// 一時的変数を導入することで、</span> + <span class="comment">// |p| で足踏みをすることもありません。</span> + <span class="notice">nsCOMPtr<</span>nsIDOMNode<span class="notice">></span> temp = p; + temp->GetNext(<span class="notice">getter_AddRefs(</span>p<span class="notice">)</span>); + } + +</pre> +</td></tr></tbody></table> +<p><code>nsCOMPtr</code> をパラレルにするのは、容易に理解できますが、生のポインタの枠組に比べて、余分な <code>AddRef</code> と <code>Release</code> を一回ずつしなければなりません。少し変形することで、コードは見づらくなりますが、(おそらく、ごくわずかですが) より効率的になります。 +</p> +<table> +<tbody><tr> +<td> +<pre class="eval"><span class="comment">// 安全で、効率的な、|nsCOMPtr| での繰り返し...</span> + +nsCOMPtr<nsIDOMNode> p = ...; +while ( p ) + { + <span class="comment">// ...</span> + nsIDOMNode* next; + p->GetNext(&next); + p = dont_AddRef(next); + } + + <span class="comment">// 見てください! これはおなじみの「生のポインタ、getterの呼び出し、</span> + <span class="comment">// |dont_AddRef| の代入」パターンです。</span> +</pre> +</td></tr></tbody></table> +<h4 id="getter_.E3.82.92.E6.9B.B8.E3.81.8F" name="getter_.E3.82.92.E6.9B.B8.E3.81.8F"> getter を書く </h4> +<h3 id=".E3.82.B3.E3.83.B3.E3.83.91.E3.82.A4.E3.83.A9.E3.81.AE.E6.82.A9.E3.81.BF.E3.81.AE.E7.A8.AE" name=".E3.82.B3.E3.83.B3.E3.83.91.E3.82.A4.E3.83.A9.E3.81.AE.E6.82.A9.E3.81.BF.E3.81.AE.E7.A8.AE"> コンパイラの悩みの種 </h3> +<div class="noinclude"> +</div> |