2011-12-27 18 views
1

std :: make_sharedを使用してvoidポインタを作成したいと思います。 make_sharedはshared_ptr(new T)よりも速いと思われ、例外を保存するmake_sharedの方法でshared_ptr(new foo)を作成するライブラリ関数があるのだろうかと思います。cpp voidポインタのmake_shared

+1

どのような種類のオブジェクトを作成しますか? –

+0

それはsupllyのためにいくつかの異なる構文が必要であるという問題です。 make_void_shared (コンストラクタparams)。私はstatic_pointer_castからパフォーマンスが得られません(make_shared (bar1、...、barn)) – ted

+1

shared_ptrにvoid *をラップするように求めていますが、実際のオブジェクトを指していますか?しかし、それのポイントは何ですか? –

答えて

14

あなたはmake_sharedに関連した効率の損失なしにshared_ptr<void>へのshared_ptr<foo>を変換することができます:

#include <memory> 

struct foo {}; 

int main() 
{ 
    std::shared_ptr<void> p = std::make_shared<foo>(); 
} 

変換はあなたが今を経由して、それを参照していても、同じメモリ割り当てでfooと参照カウントを保持しますa void*

更新

このしくみを教えてください。参照カウントを含む制御ブロックに

      +------> foo 
          |  ^
p1 ---------> (refcount, +)  | 
p2 --- foo* -----------------------+ 

p1点(実際には二つの基準カウント:強い所有者のための1つの弱い所有者のためのもの)、削除部

std::shared_ptr<foo>の一般的な構造は、二つのポインタであります、アロケータ、およびオブジェクトの「動的」型へのポインタを含む。 「ダイナミック」タイプは、shared_ptr<T>コンストラクタが見たオブジェクトのタイプです。たとえば、YTと同じでも異なってもよい)です。

p2Tshared_ptr<T>と同じTであるT*を入力しています。これをストアドオブジェクトの「静的な」タイプと考えてください。 shared_ptr<T>を参照解除すると、逆参照されるのはp2です。 shared_ptr<T>を破棄し、参照カウントがゼロになると、制御ブロック内のポインタは、fooの破壊に役立ちます。

上記の図では、コントロールブロックとfooの両方が動的に割り当てられています。 p1は所有ポインタであり、制御ブロック内のポインタは所有ポインタである。 p2は所有していないポインタです。 p2の機能は参照外です(矢印演算子、get()など)。

あなたがmake_shared<foo>()を使用する場合、実装は参照カウントや他のデータと並んで、制御ブロックにfoo権利を置くための機会を持っています

p1 ---------> (refcount, foo) 
p2 --- foo* --------------^ 

ここでの最適化が唯一の今があるということです単一割り当て:fooを埋め込む制御ブロック。

上記shared_ptr<void>に変換される場合、それが起こるすべてである:

p1 ---------> (refcount, foo) 
p2 --- void* -------------^ 

すなわちp2のタイプはfoo*からvoid*に変わります。それでおしまい。 (コピーと一時的な破壊を説明するために参照カウントをインクリメント/デクリメントすることに加えて、それは左辺値から構成することによって省略することができる)。参照カウントがゼロになると、それはp1で見つけたfooを破壊する制御ブロックです。 p2は破壊操作に参加しません。

p1は実際にはコントロールブロックの汎用ベースクラスを指しています。この基本クラスは、派生コントロールブロックに格納されている型fooを無視しています。導出された制御ブロックは、実際のオブジェクトタイプYがわかっているときにshared_ptrのコンストラクタで作成されます。しかしその後、shared_ptrcontrol_block_base*を介して制御ブロックとしか通信できません。つまり、破棄のようなものは、仮想関数呼び出しを介して発生します。

shared_ptr<void>の "move construction"は、C++ 11の右辺値shared_ptr<foo>から2つの内部ポインタをコピーするだけで、参照カウントを操作する必要はありません。右辺値shared_ptr<foo>はとにかく離れて行くことを約あるので、これは次のとおりです。

// shared_ptr<foo> constructed and destructed within this statement 
std::shared_ptr<void> p = std::make_shared<foo>(); 

これはshared_ptrコンストラクタのソースコードの中で最もはっきり見ることができます。

template<class _Tp> 
template<class _Yp> 
inline _LIBCPP_INLINE_VISIBILITY 
shared_ptr<_Tp>::shared_ptr(shared_ptr<_Yp>&& __r, 
          typename enable_if<is_convertible<_Yp*, _Tp*>::value, __nat>::type) 
     _NOEXCEPT 
    : __ptr_(__r.__ptr_), 
     __cntrl_(__r.__cntrl_) 
{ 
    __r.__ptr_ = 0; 
    __r.__cntrl_ = 0; 
} 

変換建設する前に参照カウントは1つだけです変換後の参照カウントは依然として1であり、ソースはデストラクタの実行前に何も指していません。これは、一言で言えば、移動セマンティクスの喜びです! :-)

+0

これはどのように機能しますか?コンパイラの最適化(それらに頼ることが嫌い)?私の理解は、純粋なC++では、私が期待することのできる最高のものは、移動セマンティクスであると言います。これを読んで私はもっと複雑なことが起こると期待しています。 – ted

+0

すばらしい詳細と素晴らしい答え!いくつかのオーバーヘッド(私は恐れていた)が残っているように見える(refcountの増加、p2がvoid型の新しい共有ポインタ、p2が実際の型である共有ポインタ(制御ブロックへのポインタ、オブジェクトへのポインタ)の解放)滞在してください。私が間違っているなら、私を訂正してください。 – ted

+1

@ted: 'shared_ptr 'への変換がなければ、RVO(戻り値の最適化)を使って 'p'を作成します。これはコピーを持たず、参照カウントの操作もありません。 C++ 11では、 'shared_ptr 'への変換にはほとんどコストがかかりませんが、それほどコストはかかりません。 'shared_ptr 'はまだRVO経由で作成されますが、rvalueです。この値からの 'shared_ptr 'の構成は、2つの内部ポインタを単にコピーし、r値のソースの2つの内部ポインタをゼロにするように最適化されます。これは参照カウントには触れません。 2つの荷物と4つの店舗(すべて非アトミック)は余分な費用です。 –

関連する問題