Очистка списка/вектора STL указателей
Каков самый короткий фрагмент С++, который вы можете создать, чтобы безопасно очистить вектор или список указателей? (при условии, что вам нужно называть delete на указателях?)
list<Foo*> foo_list;
Я бы предпочел не использовать Boost или обернуть мои указатели с помощью умных указателей.
Ответы
Ответ 1
Так как мы бросаем перчатку здесь... "Самый короткий кусок С++"
static bool deleteAll( Foo * theElement ) { delete theElement; return true; }
foo_list . remove_if ( deleteAll );
Я думаю, мы можем доверять людям, которые придумали STL, чтобы иметь эффективные алгоритмы. Зачем изобретать колесо?
Ответ 2
Для std::list<T*>
используйте:
while(!foo.empty()) delete foo.front(), foo.pop_front();
Для std::vector<T*>
используйте:
while(!bar.empty()) delete bar.back(), bar.pop_back();
Не знаю, почему я взял front
вместо back
для std::list
выше. Я думаю, это ощущение, что это быстрее. Но на самом деле оба являются постоянным временем:). В любом случае, включите его в функцию и получайте удовольствие:
template<typename Container>
void delete_them(Container& c) { while(!c.empty()) delete c.back(), c.pop_back(); }
Ответ 3
for(list<Foo*>::const_iterator it = foo_list.begin(); it != foo_list.end(); ++it)
{
delete *it;
}
foo_list.clear();
Ответ 4
Если вы разрешаете С++ 11, вы можете сделать очень короткую версию ответа Дугласа Ледера:
for(auto &it:foo_list) delete it; foo_list.clear();
Ответ 5
Очень опасно полагаться на код за пределами контейнера, чтобы удалить указатели. Что происходит, когда контейнер уничтожается из-за брошенного исключения, например?
Я знаю, что вы сказали, что вам не нравится повышение, но, пожалуйста, рассмотрите контейнеры с указателями указателей.
Ответ 6
template< typename T >
struct delete_ptr : public std::unary_function<T,bool>
{
bool operator()(T*pT) const { delete pT; return true; }
};
std::for_each(foo_list.begin(), foo_list.end(), delete_ptr<Foo>());
Ответ 7
Я не уверен, что метод функтора выигрывает для краткости здесь.
for( list<Foo*>::iterator i = foo_list.begin(); i != foo_list.end(); ++i )
delete *i;
Я обычно советовал бы против этого. Обертка указателей в интеллектуальных указателях или использование специализированного контейнера-указателя в целом будет более надежной. Существует множество способов удаления элементов из списка (различные варианты erase
, clear
, уничтожение списка, назначение через итератор в список и т.д.). Можете ли вы гарантировать, что все они поймают?
Ответ 8
Следующий хак удаляет указатели, когда ваш список выходит из области действия с использованием RAII или если вы вызываете list:: clear().
template <typename T>
class Deleter {
public:
Deleter(T* pointer) : pointer_(pointer) { }
Deleter(const Deleter& deleter) {
Deleter* d = const_cast<Deleter*>(&deleter);
pointer_ = d->pointer_;
d->pointer_ = 0;
}
~Deleter() { delete pointer_; }
T* pointer_;
};
Пример:
std::list<Deleter<Foo> > foo_list;
foo_list.push_back(new Foo());
foo_list.clear();
Ответ 9
По крайней мере, для списка, итерации и удаления, тогда вызов clear в конце немного неполный, так как он включает в себя перемещение списка дважды, когда вам действительно нужно сделать это только один раз. Вот немного лучше:
for (list<Foo*>::iterator i = foo_list.begin(), e = foo_list.end(); i != e; )
{
list<Foo*>::iterator tmp(i++);
delete *tmp;
foo_list.erase(tmp);
}
Тем не менее, ваш компилятор может быть достаточно умным, чтобы циклически комбинировать два в любом случае, в зависимости от того, как реализован list:: clear.
Ответ 10
for(list<Foo*>::const_iterator it = foo_list.begin(); it != foo_list.end(); it++)
{
delete *it;
}
foo_list.clear();
Есть небольшая причина, по которой вы не хотели бы этого делать - вы эффективно повторяете этот список дважды.
std:: list < > :: clear является линейным по сложности; он удаляет и уничтожает один элемент за один раз в цикле.
Принимая во внимание сказанное выше, простейшим для чтения решением, на мой взгляд, является:
while(!foo_list.empty())
{
delete foo_list.front();
foo_list.pop_front();
}
Ответ 11
На самом деле, я считаю, что STD-библиотека предоставляет прямой метод управления памятью в виде класс распределителя
Вы можете расширить базовый метод disallocate() allocator для автоматического удаления членов любого контейнера.
Я/думаю/это тот тип вещи, для которого он предназначался.
Ответ 12
Так как С++ 11:
std::vector<Type*> v;
...
std::for_each(v.begin(), v.end(), std::default_delete<Type>());
Или, если вы пишете шаблонный код и хотите не указывать конкретный тип:
std::for_each(v.begin(), v.end(),
std::default_delete<std::remove_pointer<decltype(v)::value_type>::type>());
Которая (поскольку С++ 14) может быть сокращена как:
std::for_each(v.begin(), v.end(),
std::default_delete<std::remove_pointer_t<decltype(v)::value_type>>());
Ответ 13
void remove(Foo* foo) { delete foo; }
....
for_each( foo_list.begin(), foo_list.end(), remove );
Ответ 14
for (list<Foo*>::const_iterator i = foo_list.begin(), e = foo_list.end(); i != e; ++i)
delete *i;
foo_list.clear();