オブジェクト全体またはオブジェクトへのポインターをコンテナーに格納する必要がありますか?


162

新しいシステムを一から設計する。STLを使用して、特定の長寿命オブジェクトのリストとマップを保存します。

質問:オブジェクトにコピーコンストラクターがあり、オブジェクトのコピーをSTLコンテナー内に格納していることを確認する必要がありますか、それとも通常は自分でライフとスコープを管理し、それらのオブジェクトへのポインターをSTLコンテナーに格納するだけの方が良いですか?

私はこれが詳細にいくらか不足していることを理解していますが、私はこれらの解決策の両方が可能であることを知っているので、存在する場合は「理論的な」より良い答えを探しています。

ポインターを操作することの2つの非常に明らかな欠点:1)これらのオブジェクトの割り当て/割り当て解除を、STLを超えたスコープで自分で管理する必要があります。2)一時オブジェクトをスタックに作成してコンテナに追加できません。

他に欠けているものはありますか?


36
神私はこのサイトが大好きです、これは私が今日考えていた
正確な

2
もう1つの興味深い点は、ポインターが実際にコレクションに追加されたかどうかを確認し、そうでない場合はメモリリークを回避するためにdeleteを呼び出す必要があることです... if((set.insert(pointer))。second = false) {ポインタの削除;}
javapowered 2011年

回答:


68

人々はポインタを使うことの効率性について理解しているからです。

std :: vectorの使用を検討していて、更新が少なく、頻繁にコレクションを反復処理し、それが非ポリモーフィック型のオブジェクトである場合は、参照の局所性が向上するため、オブジェクトの「コピー」がより効率的になります。

Otoh、更新が一般的な格納ポインタである場合、コピー/再配置のコストを節約できます。


7
キャッシュの局所性の観点から、ポインターをベクターに格納することは、指示先のカスタムアロケーターと共に使用すると効率的です。カスタムアロケーターは、たとえば配置newを使用するなど、キャッシュの局所性を処理する必要があります(en.wikipedia.org/wiki/Placement_syntax#Custom_allocatorsを参照)。
2011

47

これは本当にあなたの状況に依存します。

オブジェクトが小さく、オブジェクトのコピーを実行するのが軽量な場合、stlコンテナー内にデータを格納するのは簡単で、寿命の管理について心配する必要がないため、私の意見では管理が簡単です。

オブジェクトが大きく、デフォルトのコンストラクターを使用しても意味がない場合や、オブジェクトのコピーにコストがかかる場合は、ポインターを使用して保存する方法が適しています。

オブジェクトへのポインターを使用する場合は、Boost Pointer Container Libraryをご覧ください。このブーストライブラリは、動的に割り当てられたオブジェクトで使用するすべてのSTLコンテナーをラップします。

各ポインターコンテナー(ptr_vectorなど)は、コンテナーに追加されるときにオブジェクトの所有権を取得し、それらのオブジェクトの有効期間を管理します。また、参照により、ptr_コンテナ内のすべての要素にアクセスします。これにより、次のようなことができます

class BigExpensive { ... }

// create a pointer vector
ptr_vector<BigExpensive> bigVector;
bigVector.push_back( new BigExpensive( "Lexus", 57700 ) );
bigVector.push_back( new BigExpensive( "House", 15000000 );

// get a reference to the first element
MyClass& expensiveItem = bigList[0];
expensiveItem.sell();

これらのクラスはSTLコンテナーをラップし、すべてのSTLアルゴリズムで動作します。これは本当に便利です。

コンテナー内のポインターの所有権を(ほとんどのコンテナーのリリース関数を介して)呼び出し元に転送する機能もあります。


38

ポリポーリックオブジェクトを格納する場合は、常に基本クラスポインターのコレクションを使用する必要があります。

つまり、コレクションにさまざまな派生型を格納することを計画している場合は、ポインタを格納するか、スライスデーモンに食われる必要があります。


1
スライスデーモンが大好きでした。
idichekop 2017年

22

イベントから3年後にジャンプして申し訳ありませんが、ここに注意書きがあります...

私の最後の大きなプロジェクトでは、私の中心的なデータ構造は、かなり単純なオブジェクトのセットでした。プロジェクトの約1年後、要件が進化するにつれ、オブジェクトは実際には多態性である必要があることに気付きました。データ構造を基本クラスポインターのセットに修正し、オブジェクトの保存やキャストなどの付随的な損傷をすべて処理するには、数週間の困難で厄介な脳手術が必要でした。新しいコードが機能していることを納得するまでに数か月かかりました。ちなみに、これはC ++のオブジェクトモデルがどれほどうまく設計されているかについて私に一生懸命考えさせられました。

現在の大きなプロジェクトでは、私の中心的なデータ構造は、かなり単純なオブジェクトのセットです。プロジェクトから約1年(たまたま今日)、オブジェクトは実際には多態性である必要があることに気付きました。ネットに戻り、このスレッドを見つけ、ブーストポインターコンテナーライブラリへのニックのリンクを見つけました。これは、すべてを修正するために前回作成しなければならなかったものです。そのため、今回はそれを実行します。

いずれにせよ、私にとってのモラル:スペックが100%石にキャストされていない場合は、指針を求めてください。そうすれば、後で多くの作業を節約できる可能性があります。


スペックが確定することはありません。ポインタコンテナーを排他的に使用する必要があるという意味ではないと思いますが、Boostポインターコンテナーはそのオプションをはるかに魅力的にするようです。オブジェクトコンテナーをポインターコンテナーに変換する必要があると判断した場合、プログラム全体を一度にオーバーホールする必要があるかどうかは疑問です。これは、一部のデザインの場合に当てはまる可能性があります。その場合は壊れやすいデザインです。その場合は、オブジェクトコンテナの「脆弱性」を問題のせいにしないでください。
allyourcode 2013年

値のセマンティクスを使用してベクトル内のアイテムを残し、内部で多態性の動作を行うことができます。
Billy ONeal 2013

19

なぜ両方の世界を最大限に活用しないのですか?スマートポインタのコンテナ(boost::shared_ptrまたはなどstd::shared_ptr)を実行してください。メモリを管理する必要はありません。また、大量のコピー操作を処理する必要もありません。


このアプローチは、Boost Pointer Container Libraryを使用してNick Haddadが提案したものとどのように異なりますか?
ThorstenSchöning16年

10
@ThorstenSchöningstd :: shared_ptrは、ブーストへの依存関係を追加しません。
James Johnston、

共有ポインターを使用してポリモーフィズムを処理することはできないため、明示的にポインターをキャストしない限り、最終的にこのアプローチではこの機能を見逃すことになります
auserdude

11

一般に、オブジェクトをSTLコンテナーに直接格納するのが最も簡単です。最も簡単で、最も効率的で、オブジェクトを使用するのが最も簡単です。

オブジェクト自体がコピー不可能な構文を持っている場合、または抽象基本型の場合は、ポインターを格納する必要があります(最も簡単なのは、shared_ptrを使用することです)。


4
オブジェクトが大きく、要素を頻繁に移動する場合は、あまり効率的ではありません。
allyourcode 2013年

3

あなたは違いをよく理解しているようです。オブジェクトが小さくて簡単にコピーできる場合は、必ず保存してください。

そうでない場合は、ヒープに割り当てるものへのスマートポインタ(auto_ptrではなく、スマートポインタをカウントする参照)を格納することを検討します。言うまでもなく、スマートポインターを選択した場合、一時スタックに割り当てられたオブジェクトを格納することはできません(前述のとおり)。

@ Torbjörnはスライスについて優れた点を示しています。


1
ああ、決して 今まで 、これまでのauto_ptrののコレクションを作成
TorbjörnGyllebring

そうです、auto_ptrはスマートポインタではありません-カウントを参照しません。
ルーフランコ

auto_ptrにも、非破壊的なコピーセマンティクスはありません。auto_ptr from oneをto に割り当てるとanother、参照が解放されoneて変更されoneます。
Andy Finkenstadt


2

オブジェクトがコードの他の場所で参照される場合は、boost :: shared_ptrのベクターに格納します。これにより、ベクターのサイズを変更しても、オブジェクトへのポインターが有効なままになります。

すなわち:

std::vector<boost::shared_ptr<protocol> > protocols;
...
connection c(protocols[0].get()); // pointer to protocol stays valid even if resized

他の誰もオブジェクトへのポインタを格納しない場合、またはリストが拡大および縮小しない場合は、単純な古いオブジェクトとして格納するだけです。

std::vector<protocol> protocols;
connection c(protocols[0]); // value-semantics, takes a copy of the protocol

1

この質問はしばらくの間私を悩ませてきました。

ポインタの格納に頼っていますが、適用されない可能性があるいくつかの追加要件(SWIG luaラッパー)があります。

この投稿で最も重要な点は、オブジェクトを使用して自分テストすることです

今日、これを実行して、1,000万のオブジェクトのコレクションで500回、メンバー関数を呼び出す速度をテストしました。

この関数は、xdirとydir(すべてのfloatメンバー変数)に基づいてxとyを更新します。

std :: listを使用して両方のタイプのオブジェクトを保持しましたが、オブジェクトをリストに格納する方がポインターを使用するよりもわずかに速いことがわかりました。一方、パフォーマンスは非常に近いため、アプリケーションでの使用方法にかかっています。

参考までに、私のハードウェアで-O3を使用すると、ポインターが完了するまでに41秒かかり、生オブジェクトが完了するまでに30秒かかりました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.