2008-11-20 2 views
47

Каков самый короткий фрагмент C++, который вы можете создать, чтобы безопасно очистить вектор или список указателей? (Предполагается, что вы должны вызвать удаление на указатели?)Очистка списка/вектора STL

list<Foo*> foo_list; 

Я предпочел бы не использовать подталкивание или обернуть свои указатели с помощью смарт-указатели.

+2

Умные указатели (включая Boost :: shared_ptr) удалят ваши объекты в ситуациях, когда у вас будет много сложного видения, что это делается вручную. – 2010-04-19 20:45:16

+2

Это действительно опасно полагаться на код за пределами контейнера, чтобы удалить указатели. Что происходит, когда контейнер уничтожается из-за брошенного исключения, например? Я знаю, что вы сказали, что вам не нравится повышение, но, пожалуйста, рассмотрите контейнеры с указателем [boost pointer] (http://www.boost.org/doc/libs/1_37_0/libs/ptr_container/doc/ptr_container.html). – 2008-11-20 23:14:26

+0

i second you opinion – 2010-03-22 03:52:40

ответ

53

Поскольку мы бросаем перчатку здесь ... «Кратчайший кусок C++»

static bool deleteAll(Foo * theElement) { delete theElement; return true; } 

foo_list . remove_if (deleteAll); 

Я думаю, что мы можем доверять людям, которые пришли с СТЛ иметь эффективные алгоритмы. Зачем изобретать колесо?

8
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>()); 
28
for(list<Foo*>::const_iterator it = foo_list.begin(); it != foo_list.end(); ++it) 
{ 
    delete *it; 
} 
foo_list.clear(); 
+0

Поддерживается, потому что он хорошо работает и короток. Я добавил модифицированную версию вашего ответа, которая немного короче (но полагается на C++ 11). – Adisak 2014-02-12 22:06:30

+0

Вопрос не задал список констант. Зачем использовать константный итератор? – SRINI794 2014-09-16 11:35:30

+1

@ SRINI794 Я использовал константный итератор, чтобы максимально использовать для удобства использования кода, и потому, что я не собирался изменять список во время итерации, поэтому мне не нужен изменяемый итератор. – 2015-04-16 07:29:27

6

Я не уверен, что функтор подход выигрывает для краткости здесь.

for(list<Foo*>::iterator i = foo_list.begin(); i != foo_list.end(); ++i) 
    delete *i; 

Обычно я советовал этому. Обертка указателей в интеллектуальных указателях или использование специализированного контейнера-указателя в целом будет более надежной. Существует множество способов удаления элементов из списка (различные варианты erase, clear, уничтожение списка, назначение через итератор в список и т. Д.). Можете ли вы гарантировать, чтобы поймать их всех?

+0

Функторный подход может не выиграть для краткости, но это не очень важный приз. Используя функтор, вы избегаете писать свой собственный цикл, который является источником многих недостатков программного обеспечения. – 2008-11-21 15:26:26

+0

Вопрос конкретно задавался вопросом «кратчайший». Я понятия не имел, что руководство для петель является основным источником недостатков, можете ли вы предоставить ссылку? Если у члена моей команды возникла проблема с написанием ошибки для цикла, я бы предпочел не допустить его на решение functor. – 2008-11-23 11:55:02

0
for (list<Foo*>::const_iterator i = foo_list.begin(), e = foo_list.end(); i != e; ++i) 
    delete *i; 
foo_list.clear(); 
51

Для 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(); } 
13

Это действительно опасно полагаться на код снаружи контейнера для удаления ваших указателей. Что происходит, когда контейнер уничтожается из-за брошенного исключения, например?

Я знаю, что вы сказали, что вам не нравится повышение, но, пожалуйста, рассмотрите boost pointer containers.

4

По крайней мере, для списка, выполняющего итерацию и удаление, а затем вызов 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); 
} 

Тем не менее, ваш компилятор может быть достаточно умен, чтобы петля объединить два в любом случае, в зависимости от того, как список :: ясно реализовано.

3

На самом деле, я считаю, что библиотека STD обеспечивает прямой метод управления памятью в виде allocator class

Вы можете расширить метод Основного Allocator в DEALLOCATE() для автоматического удаления членов любого контейнера.

Я/думаю/это тот тип вещи, для которого он предназначен.

5

Следующий хак удаляет указатели, когда ваш список выходит из области действия с использованием 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(); 
1
void remove(Foo* foo) { delete foo; } 
.... 
for_each(foo_list.begin(), foo_list.end(), remove); 
15

Если вы позволяете C++ 11, вы можете сделать очень короткую версию ответа Дугласа Leeder в:

for(auto &it:foo_list) delete it; foo_list.clear(); 
4
for(list<Foo*>::const_iterator it = foo_list.begin(); it != foo_list.end(); it++) 
{ 
    delete *it; 
} 
foo_list.clear(); 

Там в небольшая причина, по которой вы не хотели бы этого делать - вы эффективно итерации над t он перечисляет дважды.

std :: list <> :: clear линейный по сложности; он удаляет и уничтожает один элемент за один раз в цикле.

Принимая во внимание вышесказанное самый простой для чтения решение на мой взгляд:

while(!foo_list.empty()) 
{ 
    delete foo_list.front(); 
    foo_list.pop_front(); 
} 
3

Поскольку C++ 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>()); 

Which (с C++ 14) может быть сокращен как:

std::for_each(v.begin(), v.end(), 
    std::default_delete<std::remove_pointer_t<decltype(v)::value_type>>()); 
Смежные вопросы