web-dev-qa-db-ja.com

反復しながらデータ構造を変更する

ベクトルなどのデータ構造を反復処理しながら要素を追加するとどうなりますか。これはできませんか?

私はこれを試しましたが、壊れます:

int main() {
    vector<int> x = { 1, 2, 3 };

    int j = 0;
    for (auto it = x.begin(); it != x.end(); ++it) {
        x.Push_back(j);
        j++;
        cout << j << " .. ";
    }
}
18
user620189

イテレータは、_std::vector_を変更する一部の操作によって無効になります。

他のコンテナには、イテレータが無効になるタイミングと無効にならないタイミングに関するさまざまなルールがあります。 これは投稿です (本当にあなたによる)詳細付き。

ちなみに、エントリポイント関数main()[〜#〜] must [〜#〜]return int

_int main() { ... }
_

ベクトルなどのデータ構造を反復処理しながら要素を追加するとどうなりますか。これはできませんか?

ベクトルのサイズが変更されると、イテレータは無効になります。したがって、ベクトル自体のサイズが変更されない限り、安全です。

これは避けることをお勧めします。

サイズ変更によってイテレータが無効になる理由の簡単な説明:

最初、ベクトルにはある程度の容量があり(vector::capacity()を呼び出すことでわかります)、要素を追加します。ベクトルがいっぱいになると、より大きなサイズのメモリが割り当てられ、古いメモリから要素がコピーされます。新しく割り当てられたメモリに移動し、次に古いメモリを削除します。問題は、イテレータが割り当て解除された古いメモリをまだ指していることです。これが、サイズ変更によってイテレータが無効になる方法です。

これが簡単なデモンストレーションです。 capacityがいつ変更されるかを確認してください。

_std::vector<int> v;
for(int i = 0 ; i < 100 ; i++ )
{
  std::cout <<"size = "<<v.size()<<", capacity = "<<v.capacity()<<std::endl;
  v.Push_back(i);
}       
_

部分出力:

_size = 0, capacity = 0
size = 1, capacity = 1
size = 2, capacity = 2
size = 3, capacity = 4
size = 4, capacity = 4
size = 5, capacity = 8
size = 6, capacity = 8
size = 7, capacity = 8
size = 8, capacity = 8
size = 9, capacity = 16
size = 10, capacity = 16
_

ここで完全な出力を参照してください: http://ideone.com/rQfWe

注:capacity()は、新しいメモリを割り当てずにベクトルに含めることができる要素の最大数を示します、およびsize()は、ベクトルに現在含まれている要素の数を示します。

11
Nawaz

ベクトルのサイズが変更されると、イテレータが無効になるため(ポインタをベクトルのメモリにラップするため)、一般的には悪い考えです。

また、コードが実際に何をしようとしているのかも明確ではありません。イテレータがどういうわけか無効にならなかった場合(インデックスとして実装されたと仮定します)、そこに無限ループがあると思います-常に要素を追加しているので、終わりに達することはありません。

original要素をループし、それぞれに1つずつ追加する場合、1つの解決策は、新しい要素を2番目のベクトルに追加し、最後にそれを連結することです。

vector<int> temp;

// ...

// Inside loop, do this:
temp.Push_back(j);

// ...

// After loop, do this to insert all new elements onto end of x
x.insert(x.end(), temp.begin(), temp.end());
2
Luke Halliwell

それをするのは良い考えではありません。

Push_backの後にベクターのサイズを変更する必要がある場合を考えることができます。その後、より大きなメモリスポットに移動する必要があり、イテレータは無効になります。

1
Drahakar

この方法で行う必要がある場合は、追加できるレコードの最大数をreserveできます。これにより、ベクターのサイズを変更する必要がなくなり、クラッシュを防ぐことができます。

0
dmjalund

例としてvectorを使用しましたが、イテレーターを無効にすることなく要素をプッシュバックできる他のstlコンテナーがあります。要素をstd :: listにプッシュバックする場合、既存の要素は連続して保存されないため、再割り当てする必要はありません(リストは、次のノードへのポインターによってリンクされたノードで構成されます)。したがって、イテレーターは有効なままです。それらが内部的に指すノードは、引き続き同じアドレスに存在します。

0
Xoanon