2009-09-07 16 views
5

出力は次のようになります。のSTD C++コンテナ要素の破壊と挿入行動

Instance Albert of class Foo created! 
Instance Bert of class Foo created! 
Press any key... 
Instance Albert of class Foo copied!  
Instance Albert of class Foo copied!  // why another copy? 
Instance Albert of class Foo destroyed! // and destruction? 
Press any key... 
Instance Bert of class Foo copied! 
Instance Bert of class Foo copied! 
Instance Bert of class Foo destroyed! 
Press any key...      // v1=v2 why did the albert instance not get destroyed? 
Press any key...      
Instance Bert of class A destroyed! 
Instance Bert of class A destroyed! 
Press any key...      // there's still an albert living in the void 

これは非常に奇妙なとして私を打ちます。 とにかく2度コピーされると、何かを参照として渡すのはなぜですか? なぜv1.operator =(他の)それが含んでいる要素を破壊しないのですか? shared_ptrの動作にうまく収まるでしょう。 誰かが私にその理由を教えてもらえますか?

ADDITION 私は少なくとも MEM漏れを生産していないようだ、無限ループにこれを入れて、メモリ使用量をチェックします。

追加 [OK]をクリックします。[OK]をクリックし、[OK]をクリックします。 追加すると

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

論理的なコピー回数が発生します。それなしでは、すべての単一のpush_backに対してベクトル全体を再割り当てしてコピーします(小さなベクトルでもかなり遅れていることがわかります)。 このを見て、私はもっと.reserve使用することを検討して地獄のような私の代入演算子を最適化するには:)

ADDITION:概要

  1. すべてのこれらの問題は、VC++ 2005に固有のようです。
  2. 2つのコンテナのサイズが一致する場合、実装では、古いものを破棄して新しいものをコピーする代わりに 要素のoperator =を使用して、 サウンドプラクティスのようです。サイズが異なる場合、通常の破壊とコピーが使用されます。
  3. 2005年の実装では、予備を使用する必要があります!そうでなければ、深刻ではなく、標準に準拠しない性能。
  4. これらの黒いボックスは、私が思ったよりもはるかに黒いです。
+0

リリースビルドとしてコンパイルしてみましたか? – jalf

+0

はい。同じ結果。 – AndreasT

+0

自分自身を試してみて、空のプロジェクトにペーストをコピーして、iostream、ベクトル、文字列をインクルードして追加してください。 – AndreasT

答えて

4

とにかく2度コピーされると参照として渡すのはなぜですか?

STLコンテナの種類は、必要に応じて頻繁に格納するオブジェクトをコピーできるブラックボックスと考える必要があります。たとえば、コンテナのサイズが変更されるたびに、すべてのオブジェクトがコピーされます。

push_back()のコンパイラの実装で一時的な余分なコピーが使用されている可能性があります。私のマシン(Mac OS Xのgcc)には、push_back()(プログラムの出力による)の間に余分なコピーはありません。

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

なぜv1.operator =(他の)それが含む要素を破壊しないのですか?

Foo::operator=は、引数として「BERT」インスタンスと「アルバート」インスタンスに対して呼び出されます。したがって、ここで暗黙的な破棄およびコピー操作はありません。あなたは、オペレータのための独自の実装を提供することにより、これを確認したい場合があります:

Foo& operator=(const Foo& other) { 
    cout << "Instance " << other._name << " of Foo assigned to " << _name << "!" << std::endl; 
    return *this; 
} 

これは私のマシン上で次の出力を生成します。

作成はFooの

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

+1

そして、コピーを追跡するだけで、オプティマイザがもはやそれを排除しなくなる点で、コピーのコピーがより複雑になることに注意してください。だからあなたはカウントされているために存在するctorコールを数えています! – MSalters

+0

ブラックボックスの引数は有効ですが、私はいつもこれらが愚かなブラックボックスではないと考えました:)。 もう一つ別のM $失望。 – AndreasT

+0

@MSalters:これは興味深いことです。 C++の不確実性の原則。 – stribika

3

自動生成=演算子があります。 v1 = v2を実行すると、その演算子が使用されます。その時点で、「アルバート」インスタンスの1つが「バート」になります。フーにこの機能を追加してみてください:

Foo& operator = (const Foo& rval) { 
    cout << _name << " = " << rval._name << endl; 
    _name = rval._name; 
    return *this; 
} 

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

+0

と余分なコピーとmemリークについて(実際には起こっていない、上記参照) – AndreasT

+1

この関数(または静的カウンタ)を追加すると、リークがないことがわかります。余分なコピーに関しては、STLコンテナが物事を一度だけコピーするという保証はありません。 STLの実装に応じて、値渡しやその他のものがあるかもしれません。 – stribika

1

GCCでコンパイルした場合、「ダブルコピー」は発生しません。これは、std :: vectorがVC++で実装される方法に固有のものでなければなりません。

+0

それはVS2005に固有のようだ – AndreasT

1

のVisual Studio 2008のは私に次のような出力が得られます。

Instance Albert of Foo created! 
Instance Bert of Foo created! 
Press any key to continue . . . 
Instance Albert of Foo copied! 
Press any key to continue . . . 
Instance Bert of Foo copied! 
Press any key to continue . . . 
Press any key to continue . . . << here auto-generated operator= doing its job 
Instance Bert of Foo destroyed! 
Instance Bert of Foo destroyed! << this is Albert was originally 
Press any key to continue . . .

std::vector実装はVS2005で非常に効果的でないことをしているようです。

+1

それは最も確実に真実! – AndreasT

関連する問題