#c #vector #delete-operator
#c #вектор #оператор удаления
Вопрос:
Должны ли мы удалять до или после erase
. Насколько я понимаю, оба варианта в порядке. Это правильно?
Кроме того, есть ли какой-либо случай, когда мы не захотим удалять элемент во время его стирания? Я считаю, что должно быть, в противном случае erase
будет рад взять на себя ответственность.
std::vector<foo*> bar;
...
for (vector<foo*>::iterator itr = bar.begin(); itr != bar.end(); itr )
{
delete (*itr); //before OR
bar.erase(itr);
delete (*itr); //after???
}
Комментарии:
1. Я полагаю, что у вас тоже есть
iter
, иначе цикл не завершится.2. Неустановленный контекст заключается в том, что указатели в векторе являются единственными владельцами удаляемой памяти.
Ответ №1:
«itr» должен использоваться следующим образом;
for (vector<foo*>::iterator itr = bar.begin(); itr != bar.end(); )
{
delete (*itr);
itr = bar.erase(itr);
}
Однако я бы предпочел сначала удалить все элементы, а затем очистить вектор;
for (vector<foo*>::iterator itr = bar.begin(); itr != bar.end(); itr)
delete (*itr);
bar.clear();
Комментарии:
1. 1, Я бы также выбрал вторую версию: таким образом, у меня не было бы всех этих сдвигов элементов в векторе. По крайней мере, при использовании цикла со стиранием, обратная итерация была бы более подходящей.
2. Я согласен, но я не уверен, совместим ли vector::erase с reverse_iterator (устал думать об этом до конца)
3. Вы должны использовать предпочтительное решение. Это O (n) в отличие от O (n ^ 2) .
Ответ №2:
Использование итератора для стирания элемента делает итератор недействительным. Вы должны удалить элемент до того, как он будет удален.
Вы также должны использовать возвращаемое значение из erase для вашей следующей итерации цикла.
Комментарии:
1. в целом хорошие комментарии, но не могли бы вы рассказать нам, в чем причина «Вы должны удалить элемент до того, как он будет удален». Я не понимаю, почему это должно быть так. На самом деле я бы подумал, что обратное было бы более безопасным (т. Е. в многопоточном коде). Недостатком является то, что вы должны сохранять значение указателя во временном перед вызовом erase, иначе вы не сможете его удалить. Я не вижу в этом большого преимущества, поэтому не рекомендовал бы этого, но, судя по тому, как вы сформулировали свой ответ, похоже, что удаление раньше — единственный способ.
2. @n1ck: Он спрашивал между двумя вариантами (т. е. до или после). Я просто обращался к вопросу. Конечно, вы могли бы переместить указатель на временный объект, но удаление их на месте работает так же хорошо для большинства приложений.
3. Харви: конечно, я только что сказал, что ваша формулировка, возможно, не самая лучшая, поскольку, похоже, она подразумевает, что это единственный способ. Я думаю, читатель должен быть в состоянии понять, но я просто хотел внести полную ясность, вот и все.
Ответ №3:
Кроме того, есть ли какой-либо случай, когда мы не захотим удалять элемент во время его стирания?
Как вектор может узнать, нужны ли кому-либо еще объекты, на которые указывают? Как он вообще мог узнать, что указатели хранятся в куче? Вполне возможно иметь указатели на статические или автоматические объекты в векторе или даже висячие указатели.
C 0x позволяет выразить, что указатели должны принадлежать вектору:
std::vector<std::unique_ptr<foo>> vec;
Теперь вам не нужно ничего удалять вручную. При удалении уникальных указателей удаляются и их соответствующие указатели. Контейнеры с собственными указателями очень редки в современном C .
Если у вас нет компилятора C 0x, вы можете использовать std::vector<boost::shared_ptr<foo> >
или boost::ptr_vector<foo>
вместо этого. Современные компиляторы также предоставляют shared_ptr
в пространстве имен std::tr1
или std
, если вы #include <memory>
.
Ответ №4:
Природа вектора в том, что удаление первого элемента приводит к сдвигу всего массива вперед, чтобы сократить эту операцию, попробуйте выполнить следующее:
std::vector<foo*> v1;
//...
while(!v1.empty())
{
delete v1.back();
v1.pop_back( );
}
Кстати, этот метод не делает недействительными никакие итераторы (только для удаленных элементов)
Ответ №5:
Выполнение erase
приведет к аннулированию vector
итератора. Это *iter
вызовет неопределенное поведение. Следовательно, вам нужно сделать delete
после erase
. Кроме того, вы не можете erase
использовать элементы из a vector
во время итерации по нему (по той же причине, iter
становится недействительным, поэтому iter
является недействительным). В этом случае вы можете удалить erase
вызов изнутри цикла и выполнить clear
с вектором вне цикла.