Whenever I need to add dynamically allocated object into a vector I've been doing that the following way:
class Foo { ... };
vector<Foo*> v;
v.push_back(new Foo);
// do stuff with Foo in v
// delete all Foo in v
It just worked and many others seem to do the same thing.
Today, I learned vector::push_back can throw an exception. That means the code above is not exception safe. :-( So I came up with a solution:
class Foo { ... };
vector<Foo*> v;
auto_ptr<Foo> p(new Foo);
v.push_back(p.get());
p.release();
// do stuff with Foo in v
// delete all Foo in v
But the problem is that the new way is verbose, tedious, and I see nobody's doing it. (At least not around me...)
Should I go with the new way?
Or, can I just stick with the old way?
Or, is there a better way of doing it?
If all you care about is exception-safety of this operation:
v.reserve(v.size()+1); // reserve can throw, but that doesn't matter
v.push_back(new Foo); // new can throw, that doesn't matter either.
The issue of a vector having responsibility for freeing the objects pointed to by its contents is a separate thing, I'm sure you'll get plenty of advice about that ;-)
Edit: hmm, I was going to quote the standard, but I actually can't find the necessary guarantee. What I'm looking for is that push_back
will not throw unless either (a) it has to reallocate (which we know it won't because of the capacity), or (b) a constructor of T throws (which we know it won't since T is a pointer type). Sounds reasonable, but reasonable != guaranteed.
So, unless there's a beneficial answer over on this question:
this code depends on the implementation not doing anything too "imaginative". Failing that, your solution from the question can be templated up:
template <typename T, typename Container>
void push_back_new(Container &c) {
auto_ptr<T> p(new T);
c.push_back(p.get());
p.release();
}
Usage then isn't too tedious:
struct Bar : Foo { };
vector<Foo*> v;
push_back_new<Foo>(v);
push_back_new<Bar>(v);
If it's really a factory function rather than new
then you could modify the template accordingly. Passing a lot of different parameter lists in different situations would be difficult, though.