Following an example in Accelerated C++, I created a custom STL container, which is a simplified version of std::vector, called Vec. Everything worked fine, until, emboldened by success, I tried to add a Vec::clear() that will clear the vector.
Here’s the latest class definition (only the relevant parts to this question):
template <class T>
class Vec {
public:
Vec() { create(); }
size_type size() const { return avail - data; }
size_type capacity() const { return limit - data; }
void clear();
// operators that return iterators
iterator begin() { return data; }
const_iterator begin() const { return data; }
iterator end() { return avail; }
const_iterator end() const { return avail; }
void push_back( const T& val ) {
if ( avail == limit ) grow();
unchecked_append( val );
}
private:
iterator data; // points to beginning of data
iterator avail; // points to end of initialized data
iterator limit; // points to end of data
std::allocator<T> alloc; // object to handle data allocation
void create();
// functions to support push_back()
void grow();
void unchecked_append( const T& );
};
// Creates an empty vector.
template <class T>
void Vec<T>::create() { data = avail = limit = 0; }
// All the elements of the vector are dropped: their destructors are called,
// and then they are removed from the vector container,
// leaving the container with a size of 0.
// The capacity remains the same, however.
template <class T>
void Vec<T>::clear()
{
std::cout << "capacity before clear: " << capacity() << std::endl;
std::cout << "data = " << data << " limit = " << limit << std::endl;
if (data) {
iterator it = avail;
// destroy objects in reverse order
while ( it != data ) {
alloc.destroy(--it);
}
}
data = avail = 0;
std::cout << "capacity after clear: " << capacity() << std::endl;
std::cout << "data = " << data << " limit = " << limit << std::endl;
}
// Controls how the vector should grow if it needs more space.
template <class T>
void Vec<T>::grow()
{
// Allocate twice as much storage as is currently used.
// If matrix is empty, allocate one element.
size_type new_size = std::max( 2*(limit-data), ptrdiff_t(1) );
// Allocate new space and copy existing elements
iterator new_data = alloc.allocate( new_size );
iterator new_avail = std::uninitialized_copy( data, avail, new_data );
// Deallocate old space
uncreate();
// Reset pointers to new values
data = new_data;
avail = new_avail;
limit = data + new_size;
}
// Create space for one element at the end and put given value there.
template <class T>
void Vec<T>::unchecked_append( const T& val )
{
alloc.construct( avail, val );
avail++;
}
I test this using
Vec<int> v;
for ( int i = 0; i < 100; i++ ) {
v.push_back(i);
}
std::cout << "size=" << v.size() << " capacity=" << v.capacity() << std::endl;
v.clear();
std::cout << "size=" << v.size() << " capacity=" << v.capacity() << std::endl;
I get the following output:
size=100 capacity=128
capacity before clear: 128
data = 0x100100280 limit = 0x100100480
capacity after clear: 1074004256
data = 0 limit = 0x100100480
size=0 capacity=1074004256
For some reason, clear() clobbers the limit pointer. How can this be when it doesn’t even modify it. Code looks so simple, yet I cannot see what I’m missing.
Thanks!
You’re losing (and therefore leaking)
databy setting it to 0. When you clear, you’re only taking away the available (allocated) elements, the buffer (data) stays.You should replace
data = avail = 0;withavail = data;.