typedef void* type; generic void*
class stack { \fbox{stack}
public:
stack( int n = 12 ) {
top = -1;
impl = new type[n];
}
~stack() { delete[] impl; }
bool empty() { return top == -1; }
void push( type it ) { impl[++top] = it; }
type pop() { return impl[top--]; }
private:
int top; type* impl;
};
slide: Using the void pointer
For storing the contents of the stack, an array of void pointers is created
when evaluating the constructor,
which is deleted when the destructor is called.
Provisions for dynamically enlarging the size of the stack
and for testing its bounds have been omitted, but this is easily
provided for.
An example of using the stack may look as follows:
stack s(100);
char plus = '+'; char c = 'c';
s.push(&plus); s.push(&plus); s.push(&c);
while ( !s.empty() ) {
cout << *(char*) s.pop();
}
To retrieve a value, first the pointer must be cast to a pointer
of the appropriate type, and then it may be de-referenced to deliver the
actual value.
This code clearly illustrates that the user is entirely responsible
for correctly using the stack.
Now when we look at the code,
to push elements on the stack, it is sufficient to take the address of
the value inserted.
However, when removing elements from the stack, the user must know
precisely what the type of the element popped is.
In the example, this first requires the conversion of the void pointer to
a char pointer,
and then a de-reference with an explicit cast to char.
Evidently, generic types of this kind are error-prone,
not to say ugly.