Educational Objectives: After completing this assignment the student should have the following knowledge, ability, and skills:
Operational Objectives: Create the generic container class fsu::Stack<T,N> that satisfies the interface requirements given below, along with an appropriate test harness for the class.
Deliverables: Three files stack.t, fstack.cpp, and log.txt.
builds: [2 pts each] fstack.x [student harness] x fstack_char.x x fstack_int.x x fstack_String.x x constTest.x x tests: [10 pts each] fstack_char.x b < s.com1 [0..10]: xx fstack_int.x b < s.com2 [0..10]: xx fstack_String.x b < s.com3 [0..10]: xx constTest.x [0..10]: xx code quality [-50..0]: ( x) dated submission deduction [2 pts each]: ( x) -- total [0..50]: xx
An abstract data type, abbreviated ADT, consists of three things:
The operations and axioms together should determine a unique character for the ADT, so that any two implementations should be essentially equivalent. (The word isomorphic is used to give precision to "essentially equivalent". We'll look at this in the next course.)
The stack ADT is used in many applications and has roots that pre-date the invention of high-level languages. Conceptually, stack is set of data that can be expanded, contracted, and accessed using very specific operations. The stack ADT models the "LIFO", or last-in, first-out, rule. The actual names for the stack operations may vary somewhat from one description to another, but the behavior of the abstract stack operations is well known and unambiguously understood throughout computer science. Stacks are important in many aspects of computing, ranging from hardware design and language translators (compilers) to algorithm control structures.
Typical uses of ADT Stack are (1) runtime environment for modern programming languages (facilitating recursive function calls, among other things), (2) control of the depth first search and backtracking search algorithms, (3) hardware evaluation of postfix expressions, and (4) various compiler operations, such as converting expressions from infix to postfix.
The stack abstraction has the following operations and behavior:
We will implement the stack abstraction as a C++ class template
template < typename T , size_t N > Stack;
with the following public methods:
// Stack < T , N > API void Push (const T& t); // push t onto stack; error if full T Pop (); // pop stack and return removed element; error if stack is empty T& Top (); // return top element of stack; error if stack is empty const T& Top () const; // const version size_t Size () const; // return number of elements in stack size_t Capacity () const; // return storage capacity [maximum size] of stack bool Empty () const; // return 1/true if stack is empty, 0/false if not empty void Clear (); // make the stack empty void Display (std::ostream& os, char ofc = '\0') const; // output stack contents through os void Dump (std::ostream& os); // output all private data (for development use only)
There should be a full complement of self-management features:
Stack (); // default constructor Stack (T fill); // puts "fill" in each slot of the underlying array (keeps size = 0) Stack (const Stack&); // copy constructor ~Stack (); // destructor Stack& operator = (const Stack&); // assignment operator
The element and size data will be maintained in private variables:
const size_t capacity_; // = N = size of array - fixed during life of stack T data_[N]; // array of T objects - where T objects are stored size_t size_; // current size of stack - dynamic during life of stack
The class constructors will have responsibility for initializing variables. Note that capacity_ is a constant, so it must be initialized by the constructor in the initialization list and it cannot be changed during the life of a stack object; capacity_ should be given the value passed in as the second template argument N. Because all class variables are declared at compile time, the destructor will have no responsibilities. Values stored in the data_ array and the size_ variable will be correctly maintained by the push and pop operations, using the "upper index" end of the data as the top of the stack. The data in the stack should always be the array elements in the range [0..size_), and the element data_[size_ - 1] is the top of the stack (assuming size_ > 0).
Please note that the data_ array is automatically allocated at compile time and remains allocated during the lifetime of the object. It is implicitly de-allocated just like any statically declared array, when it goes out of scope. Thus the underlying "footprint" of the stack object remains fixed as the size changes, even when the size is changed to zero. There should be no calls to operators new or delete in this implementation.
This implementation will have the requirement on clients that the maximum size required for the stack is known in advance and determined by the second template argument - see requirements below.
Create and work within a separate subdirectory cop3330/proj7. Review the COP 3330 rules found in Introduction/Work Rules.
After starting your log, copy the following files from the course directory [LIB] into your proj7 directory:
proj7/constTest.cpp proj7/deliverables.sh area51/fstack_*.x scripts/submit.sh # not needed if you have set "submit.sh" as a command in your ".bin".
The naming of these files uses the convention that _s are compiled for Sun/Solaris and _i are compiled for Intel/Linux. Use one of the sample client executables to experiment to get an understanding of how your program should behave.
Define and implement the class template fsu::Stack<T,N> in the file stack.t. Be sure to make log entries for your work sessions.
Devise a test client for Stack<T,N> that exercises the Stack interface for at least one native type and one user-defined type T. Repair your code as necessary. Put this test client in the file fstack.cpp. Be sure to make log entries for your work sessions.
Turn in stack.t, fstack.cpp, and log.txt using the submit.sh submit script.
Warning: Submit scripts do not work on the program and linprog servers. Use shell.cs.fsu.edu to submit projects. If you do not receive the second confirmation with the contents of your project, there has been a malfunction.
Stack should be a proper type, with full copy support. That is, it should have a public default constructor, destructor, copy constructor, and assignment operator. Be sure that you test the copy constructor and assignment operator.
The Stack constructor should create a stack that is empty but has the capacity
to hold N elements, where N is the second template parameter
with type size_t. Note that this parameter should be given the default
value of 100. This has the effect of making a declaration such as
fsu::Stack<int> s;
legal and create a stack with capacity 100.
Use the implementation plan discussed above. No methods or variables should be added to the class, beyond those specified above and in the implementation plan.
The Display(os, ofc) method is intended to output the contents through the std::ostream object os in bottom-to-top order. The second parameter ofc is a single output formatting character that has the default value '\0'. (The other three popular choices for ofc are ' ', '\t' and '\n'.) The implementation of Display must recognize two cases:
Thus, for example, s.Display(std::cout) would send the contents of s to standard output.
The output operator should be overloaded as follows:
template < typename T , size_t N > std::ostream& operator << (std::ostream& os, const Stack<T,N>& s) { s.Display (os, '\0'); return os; }
The overload of operator <<() should be placed in your stack header file immediately following the class definition.
The class Stack should be in the fsu namespace.
The file stack.t should be protected against multiple reads using the #ifndef ... #define ... #endif mechanism.
The test client program fstack.cpp should adequately test the functionality of stack, including the output operator. It is your responsibility to create this test program and to use it for actual testing of your stack data structure.
Your test client can be modelled on the harness fbitvect.cpp distributed as part of a previous assignment.
Keep in mind that the implementations of class template methods are in themselves template functions. For example, the implementation of the Stack method Pop() would look something like this:
template < typename T , size_t N > T Stack<T,N>::Pop() { // yada dada return ??; }
We will test your implementation stack.t using our test client fstack.cpp.
There are two versions of Stack::Top(). These are distinguished by "const" modifiers for one of the versions. The implementation code is identical for each version. The main point is that "Top()" can be called on a constant stack, but the returned reference may not be used to modify the top element. This nuance will be tested in our assessment. You can test it with two functions such as:
char ShowTop(const fsu::Stack<char>& s) { return s.Top(); } void ChangeTop(fsu::Stack<char>& s, char newTop) { s.Top() = newTop; }
Note that ShowTop has a const reference to a stack, so would be able to call the const version of Top() but not the non-const version, but that suffices. ChangeTop would need to call the non-const version in order to change the value at the top of the stack. A simple test named "constTest.cpp" is posted in the distribution directory.