Когда я делаю:

for(i=0; i<size; i++){
    //create objectA here
    vectorA.push_back(objectA);
    pvectorA.push_back(&vectorA[i]);
}

Некоторые элементы pvectorA - мусор. Однако когда я это сделаю:

for(i=0; i<size; i++){
    //create objectA here
    vectorA.push_back(objectA);

}
for(i=0; i<size; i++){
    pvectorA.push_back(&vectorA[i]);
}

Все хорошо. Почему это происходит?

1
user3794176 27 Окт 2015 в 14:32

6 ответов

Лучший ответ

Прочтите документацию по std :: vector :: push_back

Сначала описание:

Добавляет новый элемент в конец вектора после его текущего последнего элемента. Содержимое val копируется (или перемещается) в новый элемент.

Это эффективно увеличивает размер контейнера на единицу, что вызывает автоматическое перераспределение выделенного пространства хранения , если - и только если - размер нового вектора превышает текущую емкость вектора.

Затем о сроках действия итераторов:

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

Итак, когда вы добавляете объект в вектор, все указатели, указывающие на объекты в этом векторе, могут стать недействительными - если вы не гарантировали, что в векторе достаточно capacity с std::vector::reserve.

Недействительный означает, что указатель больше не указывает на действительный объект, и разыменование его будет иметь неопределенное поведение.

В последнем коде вы никогда не добавляете объекты в вектор, на который указывает указатель, после того, как вы сохранили указатели, поэтому указатели действительны.

3
Community 20 Июн 2020 в 09:12

Если вам нужен контейнер последовательности с постоянной вставкой и без недействительности итераторов, указывающих на другие элементы, используйте std :: list. Однако обратите внимание, что std :: vector часто является самой быстрой структурой данных для использования (в некоторых случаях вам понадобится предварительно отсортированный std :: vector). Одна из важных причин этого заключается в том, что массивы более удобны для кеширования, чем, например, деревья или связанные списки.

0
Erik Alapää 27 Окт 2015 в 12:55

Это связано с тем, что вектор перераспределяет свою внутреннюю память, когда она превышает свою текущую емкость; после перераспределения адрес элементов мог измениться.

Вы можете избежать перераспределения, заранее зарезервировав достаточно большой объем памяти:

vectorA.reserve(size);
//vectorB.reserve(size); // this would not hurt either
for(i=0; i<size; i++){
  //create objectA here
  vectorA.push_back(objectA);
  pvectorA.push_back(&vectorA[i]);
}

И последнее замечание: если вы можете использовать C ++ 11, emplace_back создает ваш объект на месте, следовательно, без копирования.

0
kubanrob 27 Окт 2015 в 11:57

Когда вы выполняете vectorA.push_back, вы можете заставить этот вектор перераспределить себя для увеличения емкости, что означает перемещение всего его содержимого, что означает, что любые указатели на его содержимое, которое вы сохранили, становятся недействительными.

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

Если вы не можете отказаться от всей идеи хранения указателей, но заранее знаете требуемый размер, вы можете использовать резерв перед циклом:

vectorA.reserve(size);
for(i=0; i<size; i++){
    //create objectA here
    vectorA.push_back(objectA);
    pvectorA.push_back(&vectorA[i]);
}

В этой версии указатели действительны до тех пор, пока вы не увеличите векторA дальше или не уничтожите его.

0
JSF 27 Окт 2015 в 11:54

Когда вы помещаете элементы в vectorA, он иногда заполняется, и его объекты приходится перемещать в больший блок памяти. Это изменит адрес каждого элемента.

Если pvectorA хранит указатели на исходную позицию элементов, эти указатели все равно будут указывать на старые позиции даже после того, как элементы vectorA были перемещены в новое место.

2
Bo Persson 27 Окт 2015 в 11:52

Когда вы используете push_back для добавления элемента в вектор, он копируется в вектор. Это означает, что в первом примере vectorA содержит копию objectA, а objectA выходит за пределы области видимости и удаляется сразу после закрывающей скобки. Это означает, что указатель в pvectorA указывает на адрес, который больше не обязательно содержит objectA.

Ваш второй пример не должен работать, потому что objectA вышел из области видимости после первого цикла, поэтому я не могу вам здесь помочь.

2
JHobern 27 Окт 2015 в 11:40