I'm still working on my Field class, and tried to improve my piss-poor insertion/erase performance.
However, the new function works once, then breaks catastrophically when I use it a second time.
This is the code:
template <class T>
T *Field<T>::insert(const T *pPos, const T& data)
{
// Special case: field is empty. insert should still succeed.
// Special case: Pointing to one past the end. insert should still succeed
if( empty() || pPos == last() )
{
this->push_back(data);
return (this->last() - 1);
}
/* Explanation: Find cell before which to insert new value. Push_back new
new value, then keep swapping cells until reaching *pPos and swapping it
with data. The while fails, we exit, insert successful. */
T *p = ( std::find( this->first(), this->last(), *pPos ));
if( p != last() )
{
this->push_back(data);
T *right = (this->last() - 1);
T *left = (this->last() - 2);
while( *pPos != data )
std::iter_swap( left--, right-- );
// pPos *has* to be the destination of new value, so we simply return param.
return const_cast<T*>(pPos);
}
else
throw std::range_error("Not found");
}
Calling code from main
// Field already has push_back()ed values 10, 20, 30.
field->insert( &(*field)[2], 25 ); // field is a ptr (no reason, just bad choice)
Produces this output when printed on the console.
Field: 10 20 30 // Original Field
25 // Function return value
Field: 10 20 25 30 // Correct insertion.
New calling code from main
// Field already has push_back()ed values 10, 20, 30
field->insert( &(*field)[2], 25 );
field->insert( &(*field)[3], 35 );
Produces this output when printed on the console.
Field: 10 20 30
25
35
-4.2201...e+37, 10, 15, 20, 30
Windows has triggered a breakpoint in Pg_1.exe.
This may be due to a corruption in the heap (oh shit).
No symbols are loaded for any call stack开发者_运维问答 frame.
The source code cannot be displayed.
The console then proceeds to never shutdown again until I close VSC++08 itself.
What? Why? How? What is my code doing!?
Additional Info
The Field has a size of three before the push, and a capacity of four. After two insertions, the Field is correctly increased to have a capacity of 8 (doubled), and stores five elements.
It doesn't matter where I insert my second element with insert(), it will fail the exact same way. Same output, even same number (I think) at the first cell.
Additional Code
Push_Back()
Note: This code was not changed during my refactoring. This function has always worked, so I highly doubt that this will be the problem-cause.
/* FieldImpl takes care of memory management. it stores the values v_, vused_,
and vsize_. Cells in the Field are only constructed when needed through a
placement new that is done through a helper function. */
template <class T>
void Field<T>::push_back(const T& data)
{
if( impl_.vsize_ == impl_.vused_ )
{
Field temp( (impl_.vsize_ == 0) ? 1
: (impl_.vsize_ * 2) );
while( temp.impl_.vused_ != this->impl_.vused_ )
temp.push_back( this->impl_.v_[temp.size()] );
temp.push_back(data);
impl_.Swap(temp.impl_);
}
else
{
// T *last() const { return &impl_.v_[impl_.vused_]; }
// Returns pointer to one past the last constructed block.
// variant: T *last() const { return impl_.v_; }
Helpers::construct( last(), data );
++impl_.vused_;
}
}
// ...
if( p != last() )
{
this->push_back(data);
After this line pPos
may not be a valid pointer anymore.
The console then proceeds to never shutdown again until I close VSC++08 itself.
Tried clicking the Stop button in the debugger?
From the Debugger, and from ybungalobill, it is possible to see that pPos is invalidated after a special case in the
if( p != last()
{
this->push_back(data);
part of the code. If the array is resized, the pointer is invalidated. To bridge this, I simply stored const T pos = *pPos
before the push and therefore removed the use of the *pPos pointer after a push.
Updated code:
const T pos = *pPos;
T *p = ( std::find( this->first(), this->last(), pos ) );
if( p != last() )
{
this->push_back(data);
p = ( std::find( this->first(), this->last(), pos ) );
T *right = (this->last() - 1);
T *left = (this->last() - 2);
while( *p != data )
std::iter_swap( left--, right-- );
return const_cast<T*>(p);
}
精彩评论