Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 all 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.

like image 315
stusmith Avatar asked Mar 17 '10 15:03

stusmith


2 Answers

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
like image 197
GManNickG Avatar answered Nov 08 '22 02:11

GManNickG


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

like image 25
Akanksh Avatar answered Nov 08 '22 01:11

Akanksh