You learned the concept of a stack in Section 6.11, Section 15.7.1 (stack
adapter) and Section 18.2. Recall that a node can be added to a stack and removed from a stack only at its top, so a stack is referred to as a last-in, first-out (LIFO) data structure. One way to implement a stack is as a constrained version of a linked list. In such an implementation, the link member in the last node of the stack is set to nullptr
to indicate the bottom of the stack.
The primary member functions used to manipulate a stack are push
and pop
. Function push
inserts a new node at the top of the stack. Function pop
removes a node from the top of the stack, stores the popped value in a reference variable that’s passed to the calling function and returns true
if the pop
operation was successful (false
otherwise).
Stacks have many interesting applications:
In Section 6.11, you learned that when a function call is made, the called function must know how to return to its caller, so the return address is pushed onto a stack. If a series of function calls occurs, the successive return values are pushed onto the stack in last-in, first-out order, so that each function can return to its caller. Stacks support recursive function calls in the same manner as conventional nonrecursive calls.
Stacks provide the memory for, and store the values of, automatic variables on each invocation of a function. When the function returns to its caller or throws an exception, the destructor (if any) for each local object is called, the space for that function’s automatic variables is popped off the stack and those variables are no longer known to the program.
Stacks are used by compilers in the process of evaluating expressions and generating machine-language code. The exercises explore several applications of stacks, including using them to develop your own complete working compiler.
Stack
and List
We’ll take advantage of the close relationship between lists and stacks to implement a stack class primarily by reusing our List
class template. First, we’ll implement the Stack
class template via private
inheritance from our List
class template. Then we’ll implement an identically performing Stack
class template through composition by including a List
object as a private
member of a Stack
class template.
Stack
Class Based By Inheriting from List
The program of Figs. 19.13–19.14 creates a Stack
class template (Fig. 19.13) primarily through private
inheritance (line 9) of the List
class template of Fig. 19.5. We want the Stack
to have member functions push
(Fig. 19.13, lines 12–14), pop
(lines 17–19), isStackEmpty
(lines 22–24) and printStack
(lines 27–29). Note that these are essentially the insertAtFront
, removeFromFront
, isEmpty
and print
functions of the List
class template. Of course, the List
class template contains other member functions (i.e., insertAtBack
and removeFromBack
) that we would not want to make accessible through the public
interface to the Stack
class. So when we indicate that the Stack
class template is to inherit from the List
class template, we specify private
inheritance. This makes all the List
class template’s member functions private
in the Stack
class template. When we implement the Stack
’s member functions, we then have each of these call the appropriate member function of the List
class—push
calls insertAtFront
(line 13), pop
calls removeFromFront
(line 18), isStackEmpty
calls isEmpty
(line 23) and printStack
calls print
(line 28)—this is referred to as delegation.
The explicit use of this
on lines 23 and 28 is required so the compiler can properly resolve identifiers in template definitions. A dependent name is an identifier that depends on a template parameter. For example, the call to removeFromFront
(line 18) depends on the argument data
which has a type that’s dependent on the template parameter STACKTYPE
. Resolution of dependent names occurs when the template is instantiated.
In contrast, the identifier for a function that takes no arguments like isEmpty
or print
in the List
superclass is a non-dependent name. Such identifiers are normally resolved at the point where the template is defined. If the template has not yet been instantiated, then the code for the function with the non-dependent name does not yet exist and some compilers will generate compilation errors. Adding the explicit use of this->
in lines 23 and 28 makes the calls to the base class’s member functions dependent on the template parameter and ensures that the code will compile properly.
Stack
Class TemplateThe stack class template is used in main
(Fig. 19.14) to instantiate integer stack intStack
of type Stack<int>
(line 8). Integers 0 through 2 are pushed onto intStack
(lines 13–16), then popped off intStack
(lines 21–25). The program uses the Stack
class template to create doubleStack
of type Stack<double>
(line 27). Values 1.1, 2.2 and 3.3 are pushed onto doubleStack
(lines 33–37), then popped off doubleStack
(lines 42–46).
Stack
Class With Composition of a List
ObjectAnother way to implement a Stack
class template is by reusing the List
class template through composition. Figure 19.15 is a new implementation of the Stack
class template that contains a List<STACKTYPE>
object called stackList
(line 33). This version of the Stack
class template uses class List
from Fig. 19.5. To test this class, use the driver program in Fig. 19.14, but include the new header—Stackcomposition.h
—in line 4. The output of the program is identical for both versions of class Stack
.