Pergunta

Suppose I have a std::vector<Obj *> objs (for performance reasons I have pointers not actual Objs).

I populate it with obj.push_back(new Obj(...)); repeatedly.

After I am done, I have to delete the pushed-back elements. One way is to do this:

for (std::vector<Obj *>::iterator it = objs.begin(); it != objs.end(); ++it) {
    delete *it;
}

However, I am interested if I can use for_each algorithm to do the same:

#include <algorithm>
...
for_each(objs.begin(), objs.end(), delete);

What do you think?

Foi útil?

Solução

Your problem is that delete is not a function, but rather a keyword and as such you can't take it's address.

In C++0x, there will be a std::default_delete class (used by std::unique_ptr), which you could use, or - as everyone's saying - writing one yourself would be trivial (the standard one also raises a compile error, if you try to delete an incomplete type).

#include <vector>
#include <algorithm>
#include <memory>

int main()
{
    std::vector<int*> vec;
    std::for_each(vec.begin(), vec.end(), std::default_delete<int>());
}

Outras dicas

Yes, but you need a functor:

struct delete_ptr
{
    template <typename T>
    void operator()(T* pPtr)
    {
        delete pPtr;
    }
};

std::for_each(objs.begin(), objs.end(), delete_ptr());

In C++0x, lambda's help you make functors in-place:

std::for_each(objs.begin(), objs.end(), [](Obj* pPtr){ delete pPtr; });

However, this is dangerous, in the face of exceptions. sbi has shown a solution.

While you can do this (GMan has shown a solution), having containers with naked pointers to owned resources is a strong code smell. For example, in this code:

  void foo()
  {
    std::vector<Obj *> bar;
    fill(bar);
    use(bar);
    std::for_each(objs.begin(), objs.end(), delete_ptr()); // as GMan suggests
  }

if use() throws, you'll leak objects.

So it's better to use smart pointers for this:

std::vector< std::shared_ptr<Obj> > bar;

Not exactly; for_each requires a function or object that can be invoked with (), and delete is neither a function nor an object. You will have to wrap it up in a function (or function object), perhaps like:

struct Deleter
{
    void operator()(Obj* obj) {delete obj;}
};

std::for_each(objs.begin(), objs.end(), Deleter());

But you should be very careful managing object lifetimes with raw pointers, especially if you're passing them around. You'll need to remember to delete them if you erase them from the vector, or reassign them, or if you clear the vector, or if an exception, break or function return might cause the vector to be destroyed. In general, it's always better to separate the responsibilities of resource management and resource usage.

You'd be better off with a vector of objects, unless Obj is a polymorphic base class, or the objects really are big or complicated enough that copying them will have a noticeable impact on performance. If that is the case (and you've profiled it to be sure that it's the case), you should consider a vector of smart pointers (shared_ptr, or unique_ptr if your compiler supports it), or Boost's ptr_vector.

Getting in the habit of using automatic resource management classes will save you a lot of headaches in the future.

Instead of trying to solve the deletion problem, you can make it go away completely by storing shared_ptrs in the vector, or by using boost's ptr_vector (see http://www.boost.org/doc/libs/1_39_0/libs/ptr_container/doc/tutorial.html).

for_each needs a function pointer or function object. For memory deallocation you could try &::operator delete, which would take the address of the function that deallocates memory. However, when you use the delete statement the compiler calls the destructor before calling operator delete(void*) so cleanup is actually not part of the operator delete(void*) function.

Use a functor ala GMan's answer.

Yes. Fill it with smart pointers and use vector.clear() is the easiest way.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top