5


0

私は次のような小さなプログラムを作りました。

class Foo {public:Foo(文字列名):_name(name){cout << "Fooのインスタンス" << _name << "が作成されました。 << std :: endl; ; Foo(const Foo

〜Foo(){cout << "インスタンス" << _name << "Fooの破棄!" << std :: endl; string _name; ;


int main(int argc、char ** argv){Foo albert( "Albert"); Foo bert( "Bert"); {ベクトルv1、v2;システム( "PAUSE")

v1.push_back(アルバート);システム( "PAUSE")

v2.push_back(bert);システム( "PAUSE")

v1 = v2。システム( "PAUSE") system( "PAUSE"); }

出力は次のようになります。

クラスFooのインスタンスAlbertが作成しました! クラスFooのインスタンスベルトが作成されました! どれかキーを押してください... クラスFooのインスタンスAlbertがコピーしました! クラスFooのインスタンスAlbertがコピーしました! //なぜ別のコピー? クラスFooのインスタンスAlbertが破壊しました! //そして破壊? どれかキーを押してください... クラスFooのインスタンスベルトがコピーされました! クラスFooのインスタンスベルトがコピーされました! クラスFooのインスタンスベルトが破壊されました! どれかキーを押してください... // v1 = v2なぜalbertインスタンスは破壊されなかったのですか? どれかキーを押してください... クラスAのインスタンスベルトが破壊された! クラスAのインスタンスベルトが破壊された! どれかキーを押してください... //その場にはまだアルバートが住んでいる

これは私にとって非常に奇妙なことになります。 それがとにかく2回コピーされるならばなぜ私は参照として何かを渡すことさえ煩わしくないのですか? v1.operator =(other)が含まれている要素を破壊しないのはなぜですか? shared_ptrの振る舞いとうまく合うでしょう。 誰かが私にその理由を教えてもらえますか?

*追加*私はこれを無限ループに入れてmemの使い方を調べましたが、少なくともmemリークは発生しないようです。

追加 OK、memは問題ではありません。コピーコントローラではなくoperator =を使用しているからです。 追加したとき

v1.reserve(10); v2.reserve(10);

論理コピー数が発生します。 それがなければ、それはすべてのpush_backのためにベクトル全体を再割り当てしてコピーします。 これを見て、私は.reserve moreの使用を検討し、自分の代入演算子を最適化します。hell :)のように

追加:まとめ

  1. これらの問題はすべてVC 2005に固有のものです。

  2. 2つのコンテナのサイズが一致する場合、私の実装では古い要素を破棄して新しい要素をコピーするのではなく、要素にoperator =を使用します。 サイズが異なる場合は、通常の破棄とコピーが使用されます。

  3. 2005年の実装では、人は予備金を使わなければなりません! それ以外の場合は最悪で、標準に準拠していないパフォーマンスです。

  4. これらのブラックボックスは私が思っていたよりもずっと黒いです。

4 Answer


4


―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― 2

STLコンテナタイプは、必要なだけ頻繁に保存しているオブジェクトをコピーできるブラックボックスと見なす必要があります。 たとえば、コンテナのサイズが変更されるたびに、すべてのオブジェクトがコピーされます。

あなたのコンパイラの `push_back()`の実装が一時的な追加のコピーを使うことは可能です。 私のマシン(Mac OS Xではgcc)では、 `push_back()`中に余分なコピーはありません(あなたのプログラムの出力によると)。

このコピーはSTLコード内のどこかで発生し、コピーコンストラクタ内では発生しません(参照を使用しているため)。

なぜv1.operator =(other)はそれが含んでいる要素を破壊しないのですか?* __

引数として "bert"インスタンスを使用して、 "albert"インスタンスに対して `Foo

operator =`が呼び出されます。 したがって、ここでは暗黙の破棄およびコピー操作はありません。 これを検証するには、オペレータに独自の実装を提供します。

フー

これにより、私のマシンでは次のような出力が得られます。

Instance Albert of Fooが作成しました! FooのインスタンスBertが作成されました! FooのインスタンスAlbertがコピーした! Fooのインスタンスベルトがコピーされました! Albertに割り当てられたFooのインスタンスBert! Fooのインスタンスベルトが破壊されました! FooのインスタンスAlbertが破壊されました! Fooのインスタンスベルトが破壊されました! FooのインスタンスAlbertが破壊されました!


3


自動生成された=演算子があります。 v1 = v2を実行すると、その演算子が使用されます。 その時点で、 "albert"インスタンスの1つが "bert"になります。 この機能をFooに追加してみてください。

フー

これは自動生成と同じですが、何が起こっているのかを見ることができるようにデバッグメッセージを表示します。


1


GCCでコンパイルした場合、 "二重コピー"は起こりません。 これは、VCでstd

vectorが実装されている方法に固有のものでなければなりません。


1


Visual Studio 2008では、次のような出力が表示されます。

FooのインスタンスAlbertが作成しました! FooのインスタンスBertが作成されました! 何かキーを押すと続行します 。 。 。 FooのインスタンスAlbertがコピーした! 何かキーを押すと続行します 。 。 。 Fooのインスタンスベルトがコピーされました! 何かキーを押すと続行します 。 。 。 何かキーを押すと続行します 。 。 。 <<ここで自動生成されたoperator =その仕事をしているInstance Bert of Fooは破壊されました! Fooのインスタンスベルトが破壊されました! <<これはもともとアルバートでした続行するには任意のキーを押します。 。 。
`std

vector`の実装はVS2005ではあまり効果的ではないと思われます。