Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it safe to push_back 'dynamically allocated object' to vector?

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?

like image 590
upriser Avatar asked Nov 15 '10 14:11

upriser


People also ask

Does vector Push_back make a copy?

Yes, std::vector<T>::push_back() creates a copy of the argument and stores it in the vector.

Is vector Push_back expensive?

That's a depends. Normally push_back is pretty cheap, but every now and then it has to resize to fit more elements, and that can be costly. In this case you can gain a lot by passing references or taking advantage of move semantics so that you aren't copying Object s around when you could be moving them.

Is vector Push_back slow?

The main reason why push_back is slow is because of multiple reallocation of memory. Every vector has vector::size and vector::capacity. vector::size gives the number of elements in the vector and vector::capacity gives the number of elements vector can store.

Why is Emplace_back faster than Push_back?

because emplace_back would construct the object immediately in the vector, while push_back , would first construct an anonymous object and then would copy it to the vector.


1 Answers

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:

Is std::vector::push_back permitted to throw for any reason other than failed reallocation or construction?

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.

like image 165
Steve Jessop Avatar answered Oct 20 '22 19:10

Steve Jessop