web-dev-qa-db-ja.com

std :: vectorおよびstd :: listの要素へのポインタ

あるクラスClassAの要素を持つstd::vectorがあります。さらに、std::map<key,ClassA*>を使用してインデックスを作成します。これは、いくつかのキー値を、ベクトルに含まれる要素へのポインターにマップします。

要素がベクトルの最後に追加挿入ではない)の場合、これらのポインターが有効なままである(そして同じオブジェクトを指す)という保証はありますか?つまり、次のコードは正しいでしょうか:

std::vector<ClassA> storage;
std::map<int, ClassA*> map;

for (int i=0; i<10000; ++i) {
  storage.Push_back(ClassA());
  map.insert(std::make_pair(storage.back().getKey(), &(storage.back()));
}
// map contains only valid pointers to the 'correct' elements of storage

std::listの代わりにstd::vectorを使用した場合、状況はどうなりますか?

29
MartinStettner

ベクトル-いいえ。ベクトルの容量は決して縮小しないため、参照、ポインター、およびイテレーターは、操作された要素の前の位置を参照していれば、要素が削除または変更された場合でも有効なままであることが保証されます。ただし、挿入すると、参照、ポインター、およびイテレーターが無効になる場合があります。

リスト-はい、要素を挿入および削除しても、他の要素へのポインター、参照、およびイテレーターは無効になりません

25
DumbCoder

私の知る限り、そのような保証はありません。ベクトルに要素を追加すると、要素が再割り当てされるため、マップ内のすべてのポインターが無効になります。

9
SadSido

_std::deque_を使用してください! Push_back()のみが使用されている場合、要素へのポインターは安定しています。

注:要素へのイテレータは無効になる場合があります!要素へのポインタはそうではありません。

編集:この回答は理由の詳細を説明しています: C++ dequeのイテレータはPush_front()の後に無効になりました

6
Sjoerd

保証されているかどうかはわかりませんが、実際にはstorage.reserve(needed_size)で再割り当てが発生しないようにする必要があります。

しかし、なぜインデックスを保存しないのですか?
インデックスを開始イテレータに追加することでインデックスをイテレータに変換するのは簡単です(storage.begin()+idx)。また、最初に逆参照してからアドレスを取得することで、イテレータをポインタに変えるのも簡単です(&*(storage.begin()+idx))。

3
sbi

両方にポインタを格納させるだけで、不要なオブジェクトを明示的に削除できます。

std::vector<ClassA*> storage;
std::map<int, ClassA*> map;

for (int i=0; i<10000; ++i) {
  ClassA* a = new ClassA()
  storage.Push_back(a)
  map.insert(std::make_pair(a->getKey(), a))
}
// map contains only valid pointers to the 'correct' elements of storage
1
Tom

コメントの1つから別の回答まで、必要なのはメモリ管理を一元化(容易化)することだけのようです。それが実際に当てはまる場合は、 ブーストポインタコンテナ ライブラリのようなパッケージ済みのソリューションの使用を検討し、独自のコードをできるだけ単純に保つ必要があります。

特に、 ptr_map

  1. ベクトル番号.
  2. リストの場合はい。どうやって?イテレータは、リスト内の特定のノードへのポインタとして機能します。したがって、次のような任意の構造体に値を割り当てることができます。

    リストマイリスト;

    pair <list :: iterator、int> temp;

    temp = make_pair(mylist.begin()、x);

0
Nakul Vaidya