私はC++ 11のshared_ptr
とmake_shared
を試し、make_shared
を呼び出したときに実際に何が起こっているかを見るために小さなおもちゃの例をプログラミングしました。インフラストラクチャとして、XCode4内でllvm std c ++ライブラリとともにllvm/clang 3.0を使用していました。
class Object
{
public:
Object(const string& str)
{
cout << "Constructor " << str << endl;
}
Object()
{
cout << "Default constructor" << endl;
}
~Object()
{
cout << "Destructor" << endl;
}
Object(const Object& rhs)
{
cout << "Copy constructor..." << endl;
}
};
void make_shared_example()
{
cout << "Create smart_ptr using make_shared..." << endl;
auto ptr_res1 = make_shared<Object>("make_shared");
cout << "Create smart_ptr using make_shared: done." << endl;
cout << "Create smart_ptr using new..." << endl;
shared_ptr<Object> ptr_res2(new Object("new"));
cout << "Create smart_ptr using new: done." << endl;
}
出力を見てください:
Make_sharedを使用してsmart_ptrを作成...
コンストラクターmake_shared
コンストラクタをコピー...
コンストラクタをコピー...
デストラクタ
デストラクタ
Make_shared:doneを使用してsmart_ptrを作成します。
新規を使用してsmart_ptrを作成...
コンストラクターnew
New:doneを使用してsmart_ptrを作成します。
デストラクタ
デストラクタ
make_shared
はコピーコンストラクターを2回呼び出しているようです。通常のObject
を使用してnew
にメモリを割り当てると、これは起こりません。Object
は1つだけ構築されます。
私が疑問に思っているのは次のことです。 make_shared
はnew
を使用するよりも効率的であると思われます( 1 、 2 )。理由の1つは、make_shared
が同じメモリブロックで管理されるオブジェクトとともに参照カウントを割り当てるためです。わかりました、私はポイントを得ました。もちろん、これは2つの個別の割り当て操作よりも効率的です。
それどころか、これがObject
のコピーコンストラクターへの2つの呼び出しのコストを伴う理由を理解していません。このため、everyの場合にnew
を使用した割り当てよりもmake_shared
の方が効率的であるとは確信していません。私はここで間違っていますか? Object
の移動コンストラクターを実装することもできますが、Object
を介してnew
を単に割り当てるよりも効率的かどうかはわかりません。少なくともすべての場合でそうではありません。 Object
をコピーする方が、参照カウンターにメモリを割り当てるよりも安価であれば、それは真実です。しかし、shared_ptr
-内部参照カウンタは、いくつかのプリミティブデータ型を使用して実装できますか?
コピーのオーバーヘッドが概説されているにもかかわらず、なぜmake_shared
が効率の観点から進むべき方法であるかを説明できますか?
インフラストラクチャとして、XCode4内でllvm std c ++ライブラリとともにllvm/clang 3.0を使用していました。
それはあなたの問題のようです。 C++ 11標準では、make_shared<T>
(およびallocate_shared<T>
)、セクション20.7.2.2.6:
必要条件:式:: new(pv)T(std :: forward(args)...)(pvはvoid *型を持ち、T型のオブジェクトを保持するのに適したストレージを指します)は整形式でなければなりません。 Aはアロケーター(17.6.3.5)でなければなりません。 Aのコピーコンストラクタおよびデストラクタは、例外をスローしてはなりません。
T
はnotコピー構築可能である必要があります。確かに、T
は、配置が新しく構築可能である必要さえありません。インプレースで構築可能であることが必要です。これは、make_shared<T>
はT
でできますnew
はその場です。
そのため、得られる結果は標準と一致しません。この点で、LLVMのlibc ++は壊れています。バグレポートを提出してください。
参考のために、コードをVC2010に組み込んだときに何が起こったのかを次に示します。
Create smart_ptr using make_shared...
Constructor make_shared
Create smart_ptr using make_shared: done.
Create smart_ptr using new...
Constructor new
Create smart_ptr using new: done.
Destructor
Destructor
また、Boostの元のshared_ptr
およびmake_shared
、そして私はVC2010と同じものを得た。
Libc ++の動作が壊れているため、バグレポートを提出することをお勧めします。
これら2つのバージョンを比較する必要があります。
std::shared_ptr<Object> p1 = std::make_shared<Object>("foo");
std::shared_ptr<Object> p2(new Object("foo"));
コードでは、2番目の変数は単なる共有ポインターではなく、単なるネイキッドポインターです。
肉の上に。 make_shared
is(実際には)より効率的です。これは、参照制御ブロックを実際のオブジェクトとともに1つの動的割り当てで割り当てるためです。対照的に、ネイキッドオブジェクトポインターをとるshared_ptr
のコンストラクターは、参照カウントにanother動的変数を割り当てる必要があります。トレードオフは、割り当てがアロケータによって実行されるため、make_shared
(またはそのいとこallocate_shared
)ではカスタム削除機能を指定できないことです。
(これは、オブジェクト自体の構築には影響しません。Object
の観点からは、2つのバージョンに違いはありません。より効率的なのは、管理オブジェクトではなく共有ポインタ自体です。)
念頭に置いておくべきことの1つは、最適化設定です。特にc ++に関するパフォーマンスの測定は、meaningless最適化が有効になっていない場合です。実際に最適化を使用してコンパイルしたかどうかはわかりませんので、言及する価値があると思いました。
そうは言っても、このテストで測定しているのはnotであり、_make_shared
_がより効率的です。簡単に言えば、あなたは間違ったものを測定しています:-P。
これが取引です。通常、共有ポインターを作成すると、少なくとも2つのデータメンバー(おそらくそれ以上)が含まれます。 1つはポインター用で、もう1つは参照カウント用です。この参照カウントはヒープに割り当てられます(したがって、異なるライフタイムで_shared_ptr
_で共有できるように...それが結局のところポイントです!)
したがって、std::shared_ptr<Object> p2(new Object("foo"));
のようなオブジェクトを作成している場合、少なくとも2new
の呼び出しがあります。 1つはObject
用で、もう1つは参照カウントオブジェクト用です。
_make_shared
_には、同じ連続ブロック内でポイントされたオブジェクトと参照カウントを保持するのに十分な大きさの単一のnew
を実行するオプションがあります(そうする必要はありません)。このように見えるオブジェクトを効果的に割り当てます(文字通りではなく、例証です)。
_struct T {
int reference_count;
Object object;
};
_
参照カウントとオブジェクトの存続期間は結び付けられているため(一方が他方より長く生きることは意味がありません)。このブロック全体を同時にdelete
dにすることもできます。
そのため、効率はコピーではなく割り当てにあります(他の何よりも最適化に関係していると思われます)。
明確にするために、これは_make_shared
_についてブーストが言っていることです
http://www.boost.org/doc/libs/1_43_0/libs/smart_ptr/make_shared.html
便利さとスタイルに加えて、このような関数は、オブジェクトとそれに対応する制御ブロックの両方に単一の割り当てを使用でき、shared_ptrの構築オーバーヘッドのかなりの部分を排除できるため、例外安全でかなり高速です。これにより、shared_ptrに関する効率の大きな不満の1つがなくなります。
あなたはそこに余分なコピーを取得すべきではありません。出力は次のようになります。
Create smart_ptr using make_shared...
Constructor make_shared
Create smart_ptr using make_shared: done.
Create smart_ptr using new...
Constructor new
Create smart_ptr using new: done.
Destructor
なぜ余分なコピーを入手しているのかわかりません。 (1つの「デストラクタ」を取得しすぎているので、出力を取得するために使用したコードは、投稿したコードとは異なる必要があります)
make_shared
は、2つではなく1つの動的割り当てのみを使用して実装でき、共有オブジェクトごとに1つのポインタのメモリを必要としないため、より効率的です。
編集:Xcode 4.2ではチェックしませんでしたが、Xcode 4.3では、質問に示された不正な出力ではなく、上記の正しい出力が得られます。