2012-01-05 7 views
7

、その後、私はこれを行う値の束が移入されたベクトルポインタの位置は保証されていますか?

std::vector<int> numbers; 

、私はint型のベクトルがあると言う(のエントリが43に存在する場合)

int *oneNumber = &numbers[43]; 

はに保証oneNumberです私がnumbers.resize(46)のようなものにリサイズすると言っても、常にインデックス43のintを指しているでしょうか?

期待される振る舞いがここにあるのを100%確信しているわけではありませんが、ベクトルが連続していることが保証されていることがわかりますが、その継続性がベクトルのすべてのインデックスを同じ生涯にわたって保持することを意味するかどうかはわかりません。

+0

私は必然的に、ベクター内のオブジェクトへのポインタを必要とするので、私は、常に値-種類のベクトルを作成するには慎重だ。このため。だから、 'int * ref =&numbers [43]'のような参照を保存します。後で 'push_back'を' numbers'とWHAMMOに入れます!バグ: 'ref'は無効です。なぜなら、' numbers'は完全に別のメモリスペースに再割り当てされて保存されているからです。 – bobobobo

+0

@bobobobo本当です。生成時に要素の数が分かっている(そして、いいえ、ループで値を生成するため 'std :: array'を使うことはできませんが)' reserve'はすべてのポインタ/イテレータを保証します現在の容量を超えてリサイズされない限り(再割り当てを強制し、それによって含まれる値のアドレスを移動させる可能性がある)、有効なままである。これは数回私を救ってくれました。しかし、私が覚えておくまで、私はいつも「予約する」ことを忘れて、しばらく私の頭を傷つけます。 :D –

答えて

7

から

はoneNumber常に43

はい、これは標準で保証されたインデックスでのintを指しあることが保証されます。

numbers.resize(46)のように数値をリサイズしても、

いいえ。ベクターに何かをサイズ変更、追加、または削除すると、ベクターへのすべてのアドレスとイテレータが無効になります。これは、新しいメモリロケーションでベクタを再割り当てする必要があるためです。

+0

'std :: list'を使うとどうなりますか? itemを追加するために 'list.push_back()'呼び出しのみを使用すると、無効アドレス問題を回避できますか? (そして、あなたは 'oneNumber'が指している項目を削除しませんか? – bobobobo

+0

@boboboboそれは良い質問です。私は標準が何を言っているのか分かりませんが、' std: :list'はすべてのポインタを無効にします(削除された要素を除いて)[私たちはあなたが参加したいならば今ラウンジでそれについて議論しています。](http://chat.stackoverflow.com/transcript/message/5559322 #5559322) – Mysticial

+0

私が現在使っている[別の回避策](http://stackoverflow.com/a/12771369/111307)が見つかりました。 – bobobobo

2

いいえ - ベクターは、拡大すると再割り当てできます。通常、ベクターは一度サイズが倍になります。 C++ 11標準

1 Remarks: Causes reallocation if the new size is greater than the old capacity. If no 
reallocation happens, all the iterators and references before the insertion point 
remain valid. If an exception is thrown other than by the copy constructor, move 
constructor, assignment operator, or move assignment operator of T or by any 
InputIterator operation there are no effects. If an exception is thrown by the move 
constructor of a non-CopyInsertable T, the effects are unspecified. 
4

あなたのパラノイアは正しいです。 std::vectorのサイズを変更すると、メモリの場所が変更される可能性があります。これはあなたのoneNumberが解放された古いメモリ位置を指し示していることを意味し、それにアクセスすることは未定義の動作です。

2

ベクターの容量を増やすためにベクトルのresize()またはreserve()関数を使用する場合は、配列バッキング用にメモリを再割り当てする必要があります。それが再割り当てされた場合、新しいメモリは同じアドレスに配置されないため、oneNumberに格納されたアドレスは正しい場所を指しません。

この場合も、これはベクトルが現在格納されている要素の数と要求されたサイズに依存します。詳細に応じて、ベクトルは再割り当てせずにサイズを変更できるかもしれませんが、これが当てはまるとは間違いありません。

4

ポインタは、参照は、およびstd::vector要素へのイテレータは、限り、あなたは唯一のstd::vectorの大きさは、時間ポインタ、参照でそのcapacity()を超えて成長しないstd::vectorに追加するように置く滞在することが保証されていますまたは反復子が得られた。 capacity()を超えてサイズが変更されると、std::vectorへのすべてのポインタ、参照、イテレータが無効になります。 std::vectorの末尾以外の場所に挿入すると、無効になることに注意してください。

オブジェクトを入れたままにして、最後または最初に新しい要素を挿入する場合は、std::dequeを使用できます。std::deque内の要素へのポインタと参照は、std::dequeの真ん中に挿入するか、または参照オブジェクトを削除するときに削除するときにのみ無効になります。 std::dequeの要素へのイテレータは、要素をstd::dequeに挿入するか、その要素を削除するたびに無効になります。

2

ベクトルの容量を変更すると、データは別のメモリブロックにコピーされ、元のデータは削除されます。

3

ベクトルに.resize()を呼び出すと、(古い配列)が完全に割り当てが解除され、完全に新しい配列が割り当てられ、データがその配列にコピーされる可能性があるため、ポインタが無効になります。

この回避策の1つは、です。STLベクターにポインタを保存しないでください。代わりに、は整数インデックスを格納します。

だからあなたの例では、

std::vector<int> numbers; 
int *oneNumber = &numbers[43]; // no. pointers invalidated after .resize or possibly .push_back. 
int oneNumberIndex = 43 ;  // yes. indices remain valid through .resize/.push_back 
関連する問題