Reputation: 889
Consider this implementation of std::vector::reserve() from the book "The C++ Programming Language, 4th ed., Bjarne Stroustrup:
template<class T, class A>
void vector<T,A>::reserve(size_type newalloc)
{
if (newalloc<=capacity()) return;
vector_base<T,A> b {vb.alloc,newalloc}; // get new storage
// (see PS of question for details on vb data member)
T* src = elem; // ptr to the start of old storage
T* dest = b.elem; // ptr to the start of new storage
T* end = elem+size(); // past-the-end ptr to old storage
for (; src!=end; ++src, ++dest) {
new(static_cast<void*>(dest)) T{move(*src)}; // move construct
src–>~T(); // destroy
}
swap(vb,b); // install new base (see PS if needed)
} // implicitly release old space(when b goes out of scope)
Note that in the loop, for each element in vector, at least one call is made to a ctor and a dtor(possibly triggering more such calls if the element's class has bases, or if the class or its bases have data members with ctors). (In the book, for-loop is actually a separate function, but I injected it into the reserve() here for simplicity.)
Now consider my suggested alternative:
template<class T, class A>
void vector<T,A>::reserve(size_type newalloc)
{
if (newalloc<=capacity()) return;
vector_base<T,A> b {vb.alloc,newalloc}; // get new space
memcpy(b.elem, elem, sz); // copy raw memory
// (no calls to ctors or dtors)
swap(vb,b); // install new base
} // implicitly release old space(when b goes out of scope)
To me, it seems like the end result is the same, minus the calls to ctors/dtors.
Is there a situation where this alternative would fail, and if so, where is the flaw?
P.S. I don't think it is much relevant, but here are the data members of vector
and vector_base
classes:
// used as a data member in std::vector
template<class T, class A = allocator<T> >
struct vector_base { // memory structure for vector
A alloc; // allocator
T* elem; // start of allocation
T* space; // end of element sequence, start of space allocated for possible expansion
T* last; // end of allocated space
vector_base(const A& a, typename A::size_type n)
: alloc{a}, elem{alloc.allocate(n)}, space{elem+n}, last{elem+n} { }
~vector_base() { alloc.deallocate(elem,last–elem); } // releases storage only, no calls
// to dtors: vector's responsibility
//...
};
// std::vector
template<class T, class A = allocator<T> >
class vector {
vector_base<T,A> vb; // the data is here
void destroy_elements();
public:
//...
};
Upvotes: 3
Views: 2008
Reputation: 73542
This might fail:
memcpy()
will work only if you have a vector of POD.
It will fail for all other kind of objects as it doesn't respect the semantic of the objects it copies (copy construction).
Example of issues:
memcpy()
will copy the value of the original pointer, which will not be updated correctly and continue to point to a memory region that will be released. shared_ptr
, the object count will become inconsistent (memcpy()
will duplicate the pointer without incrementing its reference count, then swap()
will make sure that the original shared pointer will be in b, which will be released, so that the shared pointer reference count will be decremented). As pointed out by T.C in the comments, as soon as your vector stores non-POD data the memcpy()
results in UB (undefined behaviour).
Upvotes: 3