2015-01-02 7 views
19

標準でstd::unique_ptrstd::shared_ptrという2つの全く異なる方法でポインタが所有している可能性があることが判明したとき、私は非常に興味があると思った。ここでcppreference::unique_ptrcppreference::shared_ptrからの宣言は次のとおりです。Deleter type in unique_ptr vs. shared_ptr

template< 
    class T, 
    class Deleter = std::default_delete<T> 
> class unique_ptr; 

template< class T > class shared_ptr; 

あなたがunique_ptrをを見ることができるようにテンプレート引数としてDELETER・オブジェクトのタイプを「節約」。また、これはDELETERが後で上のポインタから取得された方法で見ることができます。

// unique_ptr has a member function to retrieve the Deleter 
template< 
    class T, 
    class Deleter = std::default_delete<T> 
> 
Deleter& unique_ptr<T, Deleter>::get_deleter(); 

// For shared_ptr this is not a member function 
template<class Deleter, class T> 
Deleter* get_deleter(const std::shared_ptr<T>& p); 

誰かがこの違いの後ろに合理的に説明できますか?私は明らかにunique_ptrのコンセプトを支持していますが、それはなぜshared_ptrにも適用されませんか?また、なぜ後者の場合、get_deleterが非メンバ関数になるのでしょうか?ここで

+2

誰かが元の提案を掘り下げなければなりませんが、私の推測によれば、テンプレート引数としてDeleterを使用しないと、 'shared_ptr'が使いやすくなりますが、タイプ消去費用を支払う必要があります。 'get_deleter'をメンバにすると、' shared_ptr 'を使ってジェネリックコードを書くのがもっと面倒です。' get_deleter (sp) 'の代わりに' sp.template get_deleter () 'を書く必要があります。これは、 'std :: get'が非メンバである理由です。 –

+3

@ T.Cについて少し拡張しています。 「unique_ptr」の設計目標の1つは、(ほとんど)ゼロのオーバーヘッドを持つべきであるということです。 Deleterの型を消去するのは便利ですが、消去からのランタイムオーバーヘッドを招くので、 'shared_ptr'よりも' unique_ptr'の方が適切ではありません。 – wakjah

+0

その違いのため、 'shared_ptr p = make_shared 'Base'に仮想デストラクタがない場合でも正しいことを行います。 [証拠](http://coliru.stacked-crooked.com/a/f3a50f90e00d4e58)。 –

答えて

18

あなたはスマートポインタのためのオリジナルの提案を見つけることができます:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/n1450.html

は、これはかなり正確にあなたの質問に答える:

デリータが壊れないアロケーション戦略を変更し、タイプの一部ではないので、ソースまたはバイナリの互換性があり、クライアントの再コンパイルは必要ありません。

std::shared_ptrのクライアントに、例えば異なるdeletersとshared_ptrインスタンスが同一の容器に保存することができ、いくつかのより多くの柔軟性を与えるので、これもまた有用です。

shared_ptrの実装では、(参照カウントを格納するために)共有メモリブロックが必要となるため、未処理のポインタに比べていくらかのオーバーヘッドが必要であるため、タイプ消去されたデリータを追加することは大きな問題ではありませんここに。一方、オーバーヘッドが全くなく、すべてのインスタンスがデリゲータを組み込む必要があるので、それを型の一部にするのは自然なことです。

+0

タイプ消去されたDeleterはどのようにbtwと呼ばれますか? – WorldSEnder

+0

@WorldSEnder:これは実装次第ですが、通常の方法は、T *を削除するためのインタフェースを実装する(継承する)テンプレートクラスにコンクリートデリゲータをカプセル化することです。したがって、参照カウントがゼロになると、インプリメンテーションは仮想メソッドを呼び出し、このコールは埋め込みクラスにディスパッチされ、次にコンクリートデリータが呼び出されます。これはstd :: functionと同じメカニズムです。 – Horstling

+1

'std :: function'とおそらく' shared_ptr'の '生の'下位レベルの実装は、vtableベースのものよりも実際のほうが速いが、標準ライブラリはほとんど使われていないことに注意してください。 vtableの実装は、通常のC++のように理解するのが一番簡単です。 – Yakk

関連する問題