Комментарии 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
тоесть адванс дистанс и remove erase
Так хорошо, а что вы ожидали? Точнее все работает в соответствии со спекой https://en.cppreference.com/w/cpp/container/vector/erase Мне реально интересно какие у вас были ожидания от работы этого метода до того как вы разобрались с вопросом? Второй вопрос вызывающий интерес, когда вы разобрались как это работает почему вы решили написать об этом статью?
Мне кажется, несмотря на то, что вы разобрались как работает ирэйз вам все ещё не понятно почему спека на него именно такая (ну просто какие то деды решили, что так им удобней 100500 лет назад), а не так как вы ожидали. Я могу помочь вам разобраться почему спека именно такая, а не как вы ожидали.
Это ж опять открытие Америки.
#makehabrgreateagain
Элемент
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 не предусмотрн для работы с указателями, и тем более с классами! Он предназнчен только для данных, хранящихся непосредственно в элементе вектора.
std::vector::erase. Что-то здесь не так