int IStack::Pop ()
{
assert (_top > 0);
--_top;
if (_top >= 1 && 3 * _top < _capacity)
Shrink ();
return _arr [_top];
}
void IStack::Shrink ()
{
std::cout << "Shrinking stack from "
<< _capacity << " to " << 2 * _top << ".\n";
assert (2 * _top < _capacity);
// allocate new array
int * arrNew = new int [2 * _top];
// copy all entries
for (int i = 0; i < _top; ++i)
arrNew [i] = _arr [i];
_capacity = 2 * _top;
// free old memory
delete []_arr;
// substitute new array for old array
_arr = arrNew;
}
Stack2::Stack2 ()
: _last (0), _previous (0)
{}
void Stack2::Push (char const * str)
{
_previous = _last;
_last = str;
}
void Stack2::Pop ()
{
_last = _previous;
_previous = 0;
}
char const * Stack2::Top () const
{
return _last;
}
void Queue::Put (int i)
{
Link * newLink = new Link (i);
if (_tail == 0)
{
_head = newLink;
}
else
{
_tail->SetNext (newLink);
}
_tail = newLink;
}
int Queue::Get ()
{
assert (_head != 0);
int i = _head->Value ();
Link * tmp = _head;
_head = _head->Next ();
// don't delete recursively!
tmp->SetNext (0);
delete tmp;
if (_head == 0)
_tail = 0;
return i;
}
class UnlinkSeq
{
public:
UnlinkSeq (List & list)
: _list (list),
_cur (list._pHead),
_prev (0)
{}
bool AtEnd () const { return _cur == 0; }
void Advance ()
{
_prev = _cur;
_cur = _cur->Next ();
}
int Id () const { return _cur->Id (); }
void Unlink ();
private:
List & _list;
Link * _cur;
Link * _prev;
};
void UnlinkSeq::Unlink ()
{
assert (_cur != 0);
if (_prev == 0)
{
assert (_cur == _list._pHead);
_list._pHead = _cur->Next ();
}
else
{
_prev->SetNext (_cur->Next ());
}
delete _cur;
_cur = 0;
}
Notice that after calling Unlink the sequencer is no longer valid. You might try to chose an implementation which doesn't invalidate it, but there is a bigger problem. When one sequencer modifies the list, other sequencers that pointed to the same element will become invalid no matter what. So it's better to assume that any modification to a list may invalidate all sequencers, including the current one. This convention was also adopted by the C++ Standard Library.
void DLink::Unlink ()
{
assert (_pNext != 0);
assert (_pPrev != 0);
_pNext->SetPrev (_pPrev);
_pPrev->SetNext (_pNext);
_pPrev = this;
_pNext = this;
}
void List::Put (int id)
{
DLink * pLink = new DLink (id);
if (_pHead == 0)
_pHead = pLink;
else
{
pLink->SetNext (_pHead);
pLink->SetPrev (_pHead->Prev ());
_pHead->Prev ()->SetNext (pLink);
_pHead->SetPrev (pLink);
_pHead = pLink;
}
}
int List::Get ()
{
assert (_pHead != 0);
DLink * pLink = _pHead->Prev ();
if (pLink == _pHead) // last one
_pHead = 0;
pLink->Unlink ();
int result = pLink->Id ();
delete pLink;
return result;
}
class HSeq
{
public:
HSeq (HTable const & htab)
: _aList (htab._aList),
_idx (-1),
_link (0)
{
NextList ();
}
bool AtEnd () const
{ return _idx == sizeHTable && _link == 0; }
void Advance ();
int Get () { return _link->Id (); }
private:
void NextList ();
List const * _aList;
int _idx;
Link const * _link;
};
The Advance method tries to follow the current link, but if it finds the end of the current list, it searches for the next non-empty list.
void HSeq::Advance ()
{
_link = _link->Next ();
if (_link == 0)
NextList ();
}
void HSeq::NextList ()
{
assert (_link == 0);
do
{
++_idx;
} while (_aList [_idx].IsEmpty ());
if (_idx < sizeHTable)
_link = _aList [_idx].GetHead ();
else
_link = 0;
}