Предположим, что у меня есть карта STL, где значения являются указателями, и я хочу удалить их всех. Как был бы я представлять следующий код, но использование станд.:: for_each? Я счастлив за решения использовать Повышение.
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.
Вы должны создать функциональный объект:
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));
Но вы можете попробовать библиотеку контейнеров указателей , которая делает это автоматически .
Обратите внимание, что вы используете не карту, а hash_map
. Я рекомендую вам переключиться на boost 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./* ... */
Это гарантирует, что несмотря ни на что, ваш контейнер будет повторен с помощью удаления. (Например, для исключений.)
Сравните:
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
Вы пробовали использовать BOOST_FOREACH? Это должно позволить вам сделать это в строке без создания собственного вектора.
Я не тестировал следующий код, но он должен выглядеть примерно так (если не точно):
typedef stdext::hash_map<int, Foo *> MyMapType; //see comment.
BOOST_FOREACH( MyMapType::value_type& p, myMap )
{
delete p.second;
}
Ну это больше чем 1 строка, из-за typedef :)
Хорошо, я узнал, как сделать это в одну строку ... но я не думаю, что когда-либо смог бы сделать следующее в реальном коде!
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, потому что мне нравится его идея обернутого контейнера, и мой ответ, несмотря на то, что запрошен одной строкой, просто отвратителен.
Если это возможно, в карте следует использовать умные указатели.
Использование умных указателей здесь устраняет необходимость рефакторинга и отладки удаления членов. В дальнейшем нужно будет беспокоиться о меньшем управлении памятью. Каждый раз, когда я использую new/delete, я очень хорошо думаю о том, нужно ли это. Личный "запах кода" (по Мартину Фаулеру), если хотите.
Конечно, если ваш старый код возвращает карту, то подход for_each
, вероятно, будет лучшим вариантом - но если вы приложили руку к созданию карты, я бы рекомендовал использовать умные указатели.