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
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
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.
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 :)
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.