コレクションを公開するための効率的な代替手段
-
09-06-2019 - |
質問
C++ では、パフォーマンスとデータ整合性の観点から、コレクションを公開するための代替手段にはどのようなものがありますか?
私の問題は、データの内部リストを呼び出し元に返したいが、コピーを生成したくないことです。Thant は、リストへの参照を返すか、リストへのポインタを返すかを残してくれます。ただし、呼び出し元にデータを変更させることに夢中ではなく、データを読み取らせたいだけです。
- パフォーマンスとデータ整合性のどちらかを選択する必要がありますか?
- もしそうなら、一般的に一方の方向に進むのが良いのでしょうか、それともケースによって異なりますか?
- 他に代替手段はありますか?
解決
RichQさんの答え 配列やベクトルなどを使用している場合、これは合理的な手法です。
序数値によってインデックス付けされていないコレクションを使用している場合...あるいはあなたはこう思います する必要があるかもしれない 近い将来のある時点で...その場合は、独自のイテレータ型と関連するイテレータ型を公開することを検討してください。 begin()
/end()
メソッド:
class Blah
{
public:
typedef std::vector<mydata> mydata_collection;
typedef myDataCollection::const_iterator mydata_const_iterator;
// ...
mydata_const_iterator data_begin() const
{ return myPreciousData.begin(); }
mydata_const_iterator data_end() const
{ return myPreciousData.end(); }
private:
mydata_collection myPreciousData;
};
...その後、通常の方法で使用できます。
Blah blah;
for (Blah::mydata_const_iterator itr = blah.data_begin();
itr != blah.data_end();
++itr)
{
// ...
}
他のヒント
多くの場合、呼び出し元はコレクションを反復処理するためだけにアクセスしたいと考えます。Ruby の本から 1 ページを取り出して、反復をクラスのプライベートな側面にしてみましょう。
#include <algorithm>
#include <boost/function.hpp>
class Blah
{
public:
void for_each_data(const std::function<void(const mydata&)>& f) const
{
std::for_each(myPreciousData.begin(), myPreciousData.end(), f);
}
private:
typedef std::vector<mydata> mydata_collection;
mydata_collection myPreciousData;
};
このアプローチでは、自分の内部について何も暴露することはありません。あなたも 持っている コレクション。
たぶんこんな感じでしょうか?
const std::vector<mydata>& getData()
{
return _myPrivateData;
}
ここでの利点は、非常にシンプルで、C++ と同じくらい安全であることです。RobQ が示唆しているように、これをキャストすることはできますが、コピーしていない場合は、誰かがそれを防ぐことはできません。ここでは、次を使用する必要があります const_cast
, 、探している場合は非常に簡単に見つけることができます。
代わりに、反復子を使用してもほぼ同じ結果が得られますが、処理はより複雑になります。ここでイテレータを使用することの唯一の追加利点 (私が思いつく限り) は、カプセル化を改善できることです。
const 参照または共有ポインターの使用は、基礎となるコレクションの内容が時間の経過とともに変化しない場合にのみ役に立ちます。
デザインを検討してください。呼び出し元は本当に内部配列を見る必要があるのでしょうか?呼び出し元がオブジェクトに配列をどう扱うかを指示できるようにコードを再構築できますか?たとえば、呼び出し元が配列を検索しようとしている場合、所有者オブジェクトはそれを行うことができるでしょうか?
結果ベクトルへの参照を関数に渡すことができます。一部のコンパイラでは、コードがわずかに高速になる可能性があります。
最初に再設計を試み、2 番目にクリーンなソリューションを使用し、3 番目にパフォーマンスの最適化を試みることをお勧めします (必要に応じて)。
@Shog9 と @RichQ の両方のソリューションの利点の 1 つは、クライアントをコレクション実装から切り離していることです。
コレクションの種類を別のものに変更しても、クライアントは引き続き機能します。
必要なのは、データの BLOB 全体をコピーせずに読み取り専用アクセスを行うことです。いくつかの選択肢があります。
まず、上で提案したように、データ コンテナが何であっても const 参照を返すことができます。
const std::vector<T>& getData() { return mData; }
これには、具体性という点で次のような欠点があります。クラスのインターフェイスを変更せずに、データを内部的に保存する方法を変更することはできません。
次に、const 化されたポインタを実際のデータに返すことができます。
const T* getDataAt(size_t index)
{
return &mData[index];
}
これは少し優れていますが、getNumItems 呼び出しを提供し、範囲外のインデックスから保護する必要もあります。また、ポインターの定数性は簡単に捨てられ、データは読み書き可能になります。
もう 1 つのオプションは、反復子のペアを提供することですが、これはもう少し複雑です。これにはポインターと同じ利点があるほか、getNumItems 呼び出しを (必然的に) 提供する必要がなく、反復子の定数性を取り除くためにかなり多くの作業が必要になります。
おそらくこれを管理する最も簡単な方法は、Boost Range を使用することです。
typedef vector<T>::const_iterator range_iterator_type;
boost::iterator_range< range_iterator_type >& getDataRange()
{
return boost::iterator_range(mData.begin(), mData.end());
}
これには、範囲を構成したり、フィルタリングしたりできるなどの利点があります。 Webサイト.
const を使用するのは合理的な選択です。共有ポインタの実装については、boost C++ ライブラリをチェックアウトすることもできます。ポインタの利点を提供します。参照では許可されない共有ポインタを「null」に返す必要がある場合があります。
http://www.boost.org/doc/libs/1_36_0/libs/smart_ptr/smart_ptr.htm
あなたの場合、共有ポインタの型を const にして書き込みを禁止します。
持っている場合は、 std::list
単純な古いデータ (.NET で「値の型」と呼ばれるもの) の場合、そのリストへの const 参照を返すのは問題ありません (次のような邪悪なものは無視します)。 const_cast
)
持っている場合は、 std::list
ポインターの (または boost::shared_ptr
's) その場合、アイテムの変更ではなく、コレクションの変更のみが停止されます。 で コレクション。私の C++ はあまりにも錆びているので、現時点でそれに対する答えを伝えることはできません :-(
次のようなコールバックを使用することをお勧めします EnumChildWindows. 。ユーザーがデータを変更できないようにする何らかの手段を見つける必要があります。おそらく、を使用してください const
ポインタ/参照。
一方、各要素のコピーをコールバック関数に渡し、そのコピーを毎回上書きすることもできます。(コレクション全体のコピーを生成する必要はありません。私が提案しているのは、一度に 1 つの要素ずつコピーを作成することだけです。それほど時間やメモリは必要ありません)。
MyClass tmp;
for(int i = 0; i < n; i++){
tmp = elements[i];
callback(tmp);
}