2016-03-11 3 views
3

Следующий фрагмент кода обеспечивает очень странный вывод. Я ожидал, что переполнение (Python дает MemoryError)C++: push_back в std :: vector при повторении его

#include <iostream> 
#include <vector> 

int main() 
{ 
    std::vector<int> a{1,2,3}; 

    for(auto const & item : a) 
     a.push_back(item); 


    for(auto const & item : a) 
     std::cout<<item<<','; 

    return 0; 
} 

Выход: 1,2,3,1,0,3,

Как интерпретировать этот результат?

Если вы делаете подобное в Python, это дает ошибку памяти.

>>> a = range(0,20) 
>>> for i in a: 
    a.append(i) 



Traceback (most recent call last): 
    File "<pyshell#3>", line 2, in <module> 
    a.append(i) 
MemoryError 

>>> 

Этот вопрос пришел мне на ум, потому что вышеупомянутый способ написания кода считается связанным. И для связанного защитного контейнера не должно расти/сокращаться во время foreach type iteration. Итак, это непрозрачная абстракция.

Есть ли способ обернуть этот цикл foreach так, чтобы любая операция, вызывающая изменение размера/перераспределение, не допускается в тело цикла.

+0

Я подозреваю, что это не определено поведение. –

+0

@Gautam Jha Я думаю, что это немного несправедливо изменить/расширить ваш вопрос после того, как он был дан ответ ... – purpletentacle

+0

Понял, что мое намерение было не очень ясно раньше, задав новый вопрос с похожими данными, не имеет смысла. – gjha

ответ

6

В C++ добавление элементов в вектор может привести к перераспределению содержащихся данных, что приведет к аннулированию всех итераторов. Это означает, что вы не можете перебирать вектор, используя итераторы (это то, что делает цикл, основанный на диапазоне), а также вставляя новые элементы.

+0

Этот вопрос пришел мне на ум, потому что вышеупомянутый способ написания кода считается связанным. Разве это не просачивающаяся абстракция? – gjha

+0

Все в комитете C++ защищают эти петли и поручительства за безопасную связь. Без постоянного размера контейнера мы не можем достичь связанной безопасности. – gjha

3

Если вектор изменен, итератор станет недействительным.

Вы можете сделать это, если забронируете заранее.

Имейте в виду, что for range будет работать на границах итератора, определенных до внесения любых изменений. Таким образом, вы получите только копию вашего списка.

#include <iostream> 
#include <vector> 

int main() 
{ 
    std::vector<int> a{1,2,3}; 

    a.reserve(10);   // 10 should be enough to get a copy without reallocating 
    for(auto const & item : a) 
     a.push_back(item); 

    for(auto const & item : a) 
     std::cout<<item<<','; 

    return 0; 
} 

Выход:

1,2,3,1,2,3, 

Я бы не рекомендовал такой подход, как это, потому что я не считаю, что чистый или прозрачный. Тем не менее, если вы обратитесь к стандарту, такое поведение, как ожидается:

23.3.6.5 векторных модификаторов

Что касается использования insert, emplace, emplace_back, push_back.

Примечания: Вызывает перераспределение, если новый размер больше, чем у старой емкости. Если перераспределение не происходит, все итераторы и ссылки до точки вставки остаются в силе.

То есть, если перераспределение не происходит, вы можете доверять своим итераторам до точки вставки. До тех пор, пока емкость вашего вектора достаточно высока, вы можете добавить без проблем.

+0

Почему C++ не делает размер константы std :: vector без перераспределения. потому что выше код не имеет никакого смысла. – gjha

+0

'std :: vector' способен расти. Если вы хотите, чтобы размер был постоянным, вы должны «использовать std :: array» – purpletentacle

+0

Этот вопрос пришел мне на ум, потому что вышеупомянутый способ написания кода считается связанным. Разве это не просачивающаяся абстракция? – gjha

Смежные вопросы