How would I use for_each to delete every value in an STL map?
Suppose I have a STL map where the values are pointers, and I want to delete them all. How would I represent the following code, but making use of std::for_each? I'm happy for solutions to use Boost.
for( stdext::hash_map<int, Foo *>::iterator ir = myMap.begin();
ir != myMap.end();
++ir )
{
delete ir->second; // delete a开发者_如何学编程ll the (Foo *) values.
}
(I've found Boost's checked_delete
, but I'm not sure how to apply that to the pair<int, Foo *>
that the iterator represents).
(Also, for the purposes of this question, ignore the fact that storing raw pointers that need deleting in an STL container isn't very sensible).
Note: I have subsequently found and listed a one-line answer below... but the code is pretty awful so I've accepted GMan's saner answer.
You have to make a function object:
struct second_deleter
{
template <typename T>
void operator()(const T& pX) const
{
delete pX.second;
}
};
std::for_each(myMap.begin(), myMap.end(), second_deleter());
If you're using boost, you could also use the lambda library:
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));
But you might try the pointer containers library which does this automatically.
Note you are not using a map, but a hash_map
. I recommend you switch to boost's unordered_map
, which is more current. However, there doesn't seem to be a ptr_unordered_map
.
For safety, you should wrap this thing up. For example:
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;
};
And use it like:
typedef wrapped_container<
boost::unordered_map<int, Foo*>, second_deleter> my_container;
my_container.container./* ... */
This ensures no matter what, your container will be iterated through with a deleter. (For exceptions, for example.)
Compare:
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
Have you tried using BOOST_FOREACH ? That should allow you to do that in a line without creating your own functor.
I have not tested the following code but it should look something like this(if not exactly):
typedef stdext::hash_map<int, Foo *> MyMapType; //see comment.
BOOST_FOREACH( MyMapType::value_type& p, myMap )
{
delete p.second;
}
Well thats more than 1 line, due to the typedef :)
OK, I found out how to do it in one line... but I don't think I would ever actually do the following in real code!
std::for_each( mayMap.begin()
, myMap.end()
, boost::bind( &boost::checked_delete<Foo>
, boost::bind( &stdext::hash_map<int, Foo *>::value_type::second, _1 ) ) );
However I'm going to accept GMan's answer because I like his idea of a wrapped container, and my answer, despite being one line as requested, is just plain nasty.
If at all possible, you should use smart pointers in your map.
The use of smart pointers here removes the need to refactor and debug member deletion. One less memory management to worry about going forward. Any time I use new/delete I think really hard about whether that's needed. A personal "code smell" (per Martin Fowler), if you like.
Of course, if your old code returns a map, then the for_each
approach is probably your best bet - but if you had some hand in creating the map, I'd recommend using smart pointers.
精彩评论