2013-08-02 7 views
5

C++のクラスのメソッドからアイテムのコレクションを返すさまざまな方法があります。コレクションを返すときのトレードオフ

たとえば、接続経由で送信されたすべてのメッセージをリッスンするMessageSpyクラスを考えてみましょう。クライアントは、さまざまな方法でメッセージング情報にアクセスできます。

  1. のconst CollectionClass MessageSpy ::そして、getMessages()
  2. イテレータMessageSpy ::(開始)、イテレータMessageSpy ::エンド()
  3. 無効MessageSpy ::そして、getMessages(OutputIterator)
  4. 無効MessageSpy :: eachMessage(ファンクタ)
  5. 他人...

それぞれのアプローチにはトレードオフがあります。たとえば、アプローチ1では、大規模なコレクションには高価なコレクション全体をコピーする必要があります。アプローチ2では、クラスがビューには不適切なコレクションのように見えますが...

私はいつも最も適切なアプローチを選択していますので、これらのアプローチを検討する際のトレードオフ/ ?

+0

Yikes、この質問へのコメントが消えました! 「義務的な論文」へのリンクがあるコメントが1つありました。誰でもリンクを再度投稿できますか?ありがとう! –

+0

@ChristianAmmer私はそれが[this one](http://c2.com/cgi/wiki?PrematureOptimization)でした。しかし、同じテーマの周りにいくつかの "精神的な作品"周りに浮かんでいる。 – sehe

答えて

7

可能な限り最も軽量なソリューションを求める場合は、イテレータベース/コールバックベースのアプローチをお勧めします。

サプライヤの使用パターンは、コンシューマによって切り離されているためです。

特に、オブジェクトをコピーするのではなく、(N)RVOに移動したり移動したりする可能性がある場合でも、結果をコレクションにスラミングすると、完全な容量のコンテナ。

編集:優れほかに「義務論文」(そうでないなら、あなたは物事を理解したいならば、彼らはちょうど非常に便利です):デイブ・エイブラハムズWant Speed? Pass By value

  • 消費者は実際にこれをしても、消費者の場合、次善することができ、最初の数の要素

    for(auto f=myType.begin(), l=myType.end(); f!=l; ++f) 
    { 
        if (!doProcessing(*f)) 
         break; 
    } 
    
  • 後にデータの処理を停止した場合、これはやり過ぎです最終的に要素を処理する:b特定の瞬間にすべての要素をコピーする必要があるため、「現在の要素」の「スロット」を再利用してメモリ要件を削減し、キャッシュの局所性を高めます。例えば。:

    for(auto f=myType.begin(), l=myType.end(); f!=l; ++f) 
    { 
        myElementType const& slot = *f; // making the temp explicit 
        doProcessing(slot); 
    } 
    

消費者は、すべての要素を含むコレクションを望んでなかった場合は、イテレータ・インターフェースは、まだ単に優れていることに注意してください。

std::vector<myElementType> v(myType.begin(), myType.end()); 

// look: the client gets to _decide_ what container he wants! 
std::set<myElementType, myComparer> s(myType.begin(), myType.end()); 

は、そうでない場合は、この柔軟性を取得してください。最後に、スタイルのいくつかの要素が

あります性質上

  • それはイテレータを使用して要素に(定数)の参照を公開するのは簡単です。これにより、オブジェクトのスライスを回避し、クライアントが要素を多態的に使用できるようにすることがはるかに容易になります

  • イテレータスタイルのインターフェイスは、逆参照時に非const参照を返すためにオーバーロードされる可能性があります。

    for (auto& slot : myType) 
    { 
        doProcessing(slot); 
    } 
    
:あなたは範囲ベース-のためのC++ 11であなたには、いくつかのシンタックスシュガーを持つことができるの要件に準拠している場合、コンテナが返される、(直接)

  • を参照を含めることができませんでした

    最後に、(上記のように)一般的な意味では、イテレータは標準ライブラリとうまく機能します。


    コールバックスタイル(と同様に出力イテレータスタイルは)(つまり、あなたが反復を途中で中止し、戻り値を使用することができ、あなたが割り当てることなく処理を行うことができますイテレータのスタイルの多くの利点を持っていますすべての要素のコピーが前面に表示されています)。しかし、使用するにはやや柔軟性が低いようです。もちろん、特定の使用パターンを奨励する状況があるかもしれませんが、これは良い方法です。

  • 2

    私が思うだろう最初事は(あなたが何とか全く言及しなかった)

    const CollectionClass& MessageSpy::getMessages() 
    

    &です。それは修正することはできませんが自由に受け入れることができるconst参照を返します。

    実際にコピーしない限り、コピーしません。

    これが適切でない場合、Qtは、たとえば、たくさんのクラスに"implicit data sharing"を使用します。 すなわち、あなたのクラスは値によって返された "ちょっと"ですが、それらのうちの1つで書き込み操作を実行しようとするまで、内部データは共有されます。この場合、書き込もうとしているクラスでディープコピーが実行され、データが共有されなくなります。つまり、移動するデータが少なくなります。

    戻り値の最適化があるので、あまりにも大好きな人もいます。基本的には、価値のあるものを返すときには、ある状況のコンパイラの中には余分なコピーを取り除くことができ、すぐに値を渡すことができます。は参照渡しよりも速くなります。私はそれにあまり依存しませんが、コードをプロファイリングし、RVOを使用するとスピードアップが良いとわかった場合は、使用する価値があります。

    autoキーワードを使わずにC++ 03コンパイラでそれらを使用すると、#& @のロイヤルな痛みがあるので、 "iterators"はお勧めしません。長い名前または多くのtypedefs。代わりにコンテナ自体にconst参照を返します。

    +0

    Qtデータ共有を使用するlinqの+1 – sehe

    関連する問題