Как стать автором
Обновить

Комментарии 15

Добавь к классу Object ещё конструкторы и операторы копирования, перемещения с выводом в консоль запусти свой код ещё раз и посмотри результат.

П.С. при использовании метода erase все последующие объекты применяют методы перемещения чтобы занять место в памяти у удаляемого объекта. И так получается, что на место последнего никого нет, но память нужно освободить. Вот и вызывается деструктор у последнего.

Копирование в примере нигде не вызывается, вызывается только оператор перемещения.

П. С. собственно об этом и статья.

std::vector контейнер предназначенный для выделения блока памяти вот смотри:

[1][2][3][4][5]
       ^
ты хочешь удалить этот объект
что происходит в векторе:
1шаг: 4 элемент перемещаем в 3
обычно при этом в объекте ктороый перемещают затирается его старая информация
[1][2][4][0][5]
тепрь старый 4 элемент пустой но он ещё существует
2шаг: 5 элемент перемещаем в 4 элемент
[1][2][4][5][0]
теперь старый 5 элемент обнулен но всё ещё находится в памяти
3шаг: вызываем деструктор у последнего элемента
[1][2][4][5][NULL]
последний элемент удалён но нужно помнить что в векторе память уже была выделена память под 5 элементов
поэтому последний элемент всё ещё занимает память но он не инециализирован

если тебя смущает это то тебе необходимо использовать std::list или std::forward_list

list
       --> --> --> --> -->
NULL [1] [2] [3] [4] [5] NULL
   <-- <-- <-- <-- <--

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

удаление происходит так:

list
       --> --> --> -->
NULL [1] [2] [4] [5] NULL
   <-- <-- <-- <--

тоесть соседи у удаляемого объекта забывают про него и знакомятся с новым соседом

а std::forward почти как std::list но объекты не имеют связи с предыдущими соседями тоесть:

forward_list
  --> --> --> --> -->
[1] [2] [3] [4] [5] NULL

Но из-за такого распределения памяти выходит проблематичным получение произвольного объекта по индексу

Лучше изучите стандартные контейнеры STL

При работе с std::vector часто возникает задача - удалить из него элемент (или несколько элементов).

Тогда на помощь приходит использование другого контейнера.

Метод всë правильно делает.

Но использование такого удаления - это даже не тревожный звоночек, а пожарная сирена.

Либо swap с back + pop, либо другой контейнер, либо меняй работу с данными.

можно еще через дистанс удалить итератор из любого места вектора

тоесть на сколько помню чтобы не было таких моментов можно удалять уже при помощи итераторов и добавлять в нужное место

итераторы на векторе помойму если судить по описанию Random Access

https://www.geeksforgeeks.org/iterators-c-stl/

Так хорошо, а что вы ожидали? Точнее все работает в соответствии со спекой https://en.cppreference.com/w/cpp/container/vector/erase Мне реально интересно какие у вас были ожидания от работы этого метода до того как вы разобрались с вопросом? Второй вопрос вызывающий интерес, когда вы разобрались как это работает почему вы решили написать об этом статью?

Мне кажется, несмотря на то, что вы разобрались как работает ирэйз вам все ещё не понятно почему спека на него именно такая (ну просто какие то деды решили, что так им удобней 100500 лет назад), а не так как вы ожидали. Я могу помочь вам разобраться почему спека именно такая, а не как вы ожидали.

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

Элемент 3 не удаляется, а элемент 5 удаляется дважды.

Это неверный вывод. 5-й элемент на тот момент перемещён в 4-й. А тот, который был в 5-м, находится перед удалением в состоянии, который в общем случае называется "moved from". (как это по-русски... перемещённое?) Это валидное, но неопределённое состояние. И то, что в m_id там в данном случае осталось 5 проистекает из того, что m_id это простой тип, не поддерживающий перемещение - для int обмен медленнее копирования.

Если заменить m_id на тип с перемещением(напр. shared_ptr), то там на момент вызова деструктора скорее всего оказался бы №3, перемещённый туда обменами по цепочке. Но это строго говоря не гарантия.

В общем всё работает так, как и должно. А ручное добавление конструктора и оператора перемещения в класс ничего не добавило в данном случае, просто поменяло одно валидное неопределённое состояние объекта на другое на момент удаления из контейнера.

Написал бы хотя бы про std::remove_if, в связке с std erase, тем более в стандарте есть четкое описание инвалидации итераторов при вызове функции erase, вон для русских же написно : Iterators (including the end() iterator) and references to the elements at or after the point of the erase are invalidated.

Класс vector не предусмотрн для работы с указателями, и тем более с классами! Он предназнчен только для данных, хранящихся непосредственно в элементе вектора.

Очень интересно, а почему вы так думаете и чем пользуйтесь когда вам надо с классами или указателями работать?

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации