2010-03-17 4 views
7

Предположим, у меня есть карта STL, где значения являются указателями, и я хочу удалить их все. Как я могу представить следующий код, но используя std :: for_each? Я рад за решения использовать Boost.Как использовать for_each для удаления каждого значения на карте STL?

for(stdext::hash_map<int, Foo *>::iterator ir = myMap.begin(); 
    ir != myMap.end(); 
    ++ir) 
{ 
    delete ir->second; // delete all the (Foo *) values. 
} 

(я нашел подталкивания-х checked_delete, но я не знаю, как применить это к pair<int, Foo *>, который представляет итератор).

(Кроме того, для целей этого вопроса игнорируйте тот факт, что хранение необработанных указателей, требующих удаления в контейнере STL, не очень разумно).

Примечание: после этого я нашел и перечислил однострочный ответ ниже ... но код довольно ужасный, поэтому я принял более здравый ответ GMan.

+0

Приятное использование операции preincrement на вашем итераторе! –

ответ

14

Вы должны сделать функциональный объект:

struct second_deleter 
{ 
    template <typename T> 
    void operator()(const T& pX) const 
    { 
     delete pX.second; 
    } 
}; 

std::for_each(myMap.begin(), myMap.end(), second_deleter()); 

Если вы используете импульс, вы можете также использовать библиотеку лямбда:

namespace bl = boost::lambda; 
std::for_each(myMap.begin(), myMap.end(), second_deleter(), 
       bl::bind(bl::delete_ptr(), 
       bl::bind(std::select2nd<myMap::value_type>(), _1)); 

Но вы можете попробовать pointer containers библиотеку, делает это автоматически.

Обратите внимание, что вы не используете карту, но hash_map. Я рекомендую вам переключиться на unordered_map, что более актуально. Однако, похоже, нет ptr_unordered_map.

Для обеспечения безопасности вы должны обернуть это. Например:

template <typename T, typename Deleter> 
struct wrapped_container 
{ 
    typedef T container_type; 
    typedef Deleter deleter_type; 

    wrapped_container(const T& pContainer) : 
    container(pContainer) 
    {} 

    ~wrapped_container(void) 
    { 
     std::for_each(container.begin(), container.end(), deleter_type()); 
    } 

    T container; 
}; 

И использовать его как:

typedef wrapped_container< 
      boost::unordered_map<int, Foo*>, second_deleter> my_container; 

my_container.container./* ... */ 

Это гарантирует, независимо от того, ваш контейнер будет итерация с Deleter. (. Для исключения, к примеру)

Сравнить:

std::vector<int*> v; 
v.push_back(new int); 

throw "leaks!"; // nothing in vector is deleted 

wrapped_container<std::vector<int*> > v; 
v.container.push_back(new int); 

throw "no leaks!"; // wrapped_container destructs, deletes elements 
+0

Я подумал, что это может быть что-то вроде этого. Я надеялся, что это можно будет сделать без определения моего собственного типа. Можно ли использовать boost :: bind или select2nd или somesuch, интересно? – stusmith

+0

не означает ли это технически вызывать UB, если карта решает перебалансировать себя и, таким образом, начинает копировать удаленные указатели? может быть, и их тоже? - просто заметил его hash_map, а не карту, поэтому он не будет повторно балансировать. –

+0

@jk: это не удаляет что-либо с карты, просто удаляя объекты, на которые указывает. –

0

Если это возможно, почему бы вам не использовать смарт-указатели на вашей карте?

+0

В какой-то момент это должно быть возможно. На данный момент я рефакторинг довольно старого кода. По правде говоря, мне было просто любопытно, могу ли я сделать это в одной строке кода. Но вы абсолютно правы, в конце концов я бы хотел туда добраться ... но это часть миллиона строк 15-летнего кода. – stusmith

+0

Я вижу вашу точку зрения, но использование умных указателей устраняет необходимость реорганизации и отладки удаления элемента. Еще одно управление памятью беспокоится о том, чтобы идти вперед. Каждый раз, когда я использую new/delete, я очень сильно думаю о том, что это необходимо. Личный «запах кода» (за Мартина Фаулера), если хотите. –

+0

Конечно, если ваш старый код возвращает карту, то подход 'for_each', вероятно, лучший выбор - но если бы у вас была какая-то рука в создании карты, я бы рекомендовал использовать интеллектуальные указатели. – Jacob

3

Вы пытались использовать BOOST_FOREACH? Это должно позволить вам сделать это в строке, не создавая свой собственный функтор.

Я не тестировал следующий код, но он должен выглядеть примерно так (если не точно):

typedef stdext::hash_map<int, Foo *> MyMapType; //see comment. 
BOOST_FOREACH(MyMapType::value_type& p, myMap) 
{ 
    delete p.second; 
} 

Ну то будет более чем на 1 линии, в связи с ЬурейеЕ :)

+0

Вообще звук. К сожалению, это макрос и не может обрабатывать запятые между '<>' – UncleBens

+1

ах да, потребуется typedef, извините, что больше не одна строка. – Akanksh

0

OK, Я узнал, как это сделать в одной строке ... но я не думаю, что когда-либо делал бы это в реальном коде!

std::for_each(mayMap.begin() 
      , myMap.end() 
      , boost::bind(&boost::checked_delete<Foo> 
          , boost::bind(&stdext::hash_map<int, Foo *>::value_type::second, _1))); 

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

Смежные вопросы