I am inheriting an interface, and implementing a virtual function that is supposed to do some work on a list of dynamically allocated objects. The first step is to remove duplicates from the list based on some custom equivalence criteria:
class Foo { /* ... */ };
struct FooLess
{
bool operator()(const Foo *lhs, const Foo *rhs);
}
struct FooEqual
{
bool operator()(const Foo *lhs, const Foo *rhs);
}
void doStuff(std::list<Foo*> &foos)
{
// use the sort + unique idiom to find and erase duplicates
FooLess less;
FooEqual equal;
foos.sort( foos.begin(), foos.end(), less );
foos.erase(
std::unique( foos.begin(), foos.end(), equal ),
foos.end() ); // memory leak!
}
The problem is that using sort
+ unique
doesn't clean up the memory, and the elements to be erase
d have unspecified values after unique
, so I cannot perform the cleanup myself before erase
ing. I was considering something like this:
void doStuff(std::list<Foo*> &foos)
{
// make a temporary copy of the input as a list of auto_ptr's
std::list<auto_ptr<Foo>> auto_foos;
for (std::list<Foo>::iterator it = foos.begin(); it != foos.end(); ++it)
auto_foos.push_back(auto_ptr<Foo>(*it));
foos.clear();
FooLess less; // would need to change implementation to work on auto_ptr<Foo>
FooEqual equal; // likewise
auto_foos.sort( auto_foos.begin(), auto_foos.end(), less );
auto_foos.erase(
std::unique( auto_foos.begin(), auto_foos.end(), equal ),
auto_foos.end() ); // okay now, duplicates deallocated
// transfer ownership of the remaining objects back
for (std::list<auto_ptr<Foo>>::iterator it = auto_foos.begin();
it != auto_foos.end(); ++it)
{ foos.push_back(it->get()); it->release(); }
}
Will this be okay, or am I missing something?
I am not able to use C++11 (Boost might be possible) or change the function signature to accept a list of straightforward Foo
s.
There are generally the following methods you can use in C++98:
std::auto_ptr
can't do. There was an old version of that thing, which contained an additional field of type bool
that marked ownership. It was marked mutable, so it could be modified also in the object being read from when copying. The object was deleted at the end only if owned
was true. Something like:==
template <class T> class owning_ptr
{
T* ptr;
mutable bool owns;
public:
void operator =(T* src) { ptr = src; owns = true; }
owning_ptr(const owning_ptr& other)
{
// copy the pointer, but STEAL ownership!
ptr = other.ptr; owns = other.owns; other.owns = false;
}
T* release() { owns = false; return ptr; }
~owning_ptr() { if ( owns ) delete ptr; }
/* ... some lacking stuff ..*/
};
You may try out boost::shared_ptr
Instead of std::unique
, you may try to do std::adjacent_find
in a loop. Then you'll just find all elements that are "the same" as by your equal
. If there's more than one element, you will erase them in place (you are allowed to do it because it's a list, so iterators remain valid).