Project 1: Llama Stacks

Due: Tuesday, October 4, 8:59:59pm

Corrections and Addenda


The objective of this programming assignment is to have you review C++ programming using following features: object-oriented design, dynamic memory allocation, pointer manipulation, exceptions and templates.


The premise of this assignment is that you are using a system where calls to new and delete for dynamic memory allocation and de-allocation are very slow. They are slow enough that it is worth your while to consider data structures that reduce the number of calls to new and delete.

The data structure we are considering is a stack. This can be implemented as an array or as a linked list with advantages and disadvantages to both. However, your application does not allow you to determine the maximum size of the stack. If you implemented the stack using an array, you may run out of storage if the stack exceeds the size of the array. You can dynamically allocate a bigger array and copy the old stack, but the time for copying is prohibitively slow. Using a standard linked list would involve a call to new or delete for each push or pop instruction. This is also too slow.

Instead of a simple linked list, we will use a linked list where each node contains an array of items. The array will be of fixed size, say for example, 100. This linked list will start out with a single node that holds an array of 100 items. So, a single node can hold up to 100 items of the stack. That means we can push items on the stack and pop items off the stack without calling new or delete until the stack grows larger than 100. If that happens, we will add a node to the linked list. The result is a stack that holds 200 items. Adding space for 100 more items involved just one call to new.

There's a catch. Suppose our stack has 101 items and we do a pop. Now we only have 100 items, what do we do with the linked list? We can remove and deallocate the node that used to hold items 101 through 200. If we do that, what happens if we push right after the pop? We would have to add a node to the linked list again. If there's a sequence of push and pop instructions that causes the stack size to flip between 100 and 101, then we are left with the situation that every push and pop instruction results in a call to new or delete. Then there would be no advantage to our new data structure.

The solution is that we should not immediately remove a node when our stack size drops from 101 to 100. Instead, we should wait until the stack size drops much further, until it drops to 50 items (which requires at least 50 pop instructions to achieve). When the stack size is between 100 and 51, we will keep the "extra" node around in case the stack size grows to 101 again. Then we can use the space in the extra node and bypass the need to call new.

To recap, if our stack size reaches 101 for the first time, we add a node to the linked list. The stack size can increase and decrease, but we do not deallocate a node until the stack size drops to 50. Also, we won't have to allocate a new node until the stack size increases past 200. In general, we follow this scheme whenever a node is filled --- i.e., when the stack size grows past 200, 300, 400, ... This strategy prevents us from having to call new or delete very often. It also allows our stack to grow and shrink without having to copy the entire stack to a new location.

Let's call this data structure a Llama Stack (short for Linked-List Array Mixed Amalgamated Stack).


Your assignment is to implement and test a templated Llama Stack in C++. You should use the following two header files:

#ifndef _LLAMANODE_H_ #define _LLAMANODE_H_ /* File: LlamaNode.h UMBC CMSC 341 Fall 2016 Project 1 This file has the class declaration for the LlamaNode class for Project 1. See project description for details. This file should not be modified in ANY way. When your program is graded, it will be compiled with the original version of this file. Your program must work with the original. */ #include <iostream> using namespace std ; template <class T, int LN_SIZE> // forward class declaration class Llama ; template <class T, int LN_SIZE> class LlamaNode { friend class Llama<T,LN_SIZE> ; public: LlamaNode() ; ~LlamaNode() ; static void report() ; private: static int newCount ; // # of times constructor was called static int deleteCount ; // # of times desctructor was called T arr[LN_SIZE] ; LlamaNode *m_next ; } ; #include "LlamaNode.cpp" #endif
#ifndef _LLAMA_H_ #define _LLAMA_H_ /* File: Llama.h UMBC CMSC 341 Fall 2016 Project 1 This file has the class declaration for the LlamaNode class for Project 1. See project description for details. You may add public and private data members to the Llama class. You may add public and private member functions to the Llama class. */ #include <stdexcept> #include <string> #include "LlamaNode.h" using namespace std ; class LlamaUnderflow : public std::out_of_range { public: LlamaUnderflow(const string& what) : std::out_of_range(what) { } } ; template <class T, int LN_SIZE> class Llama { public: Llama() ; Llama(const Llama<T,LN_SIZE>& other) ; // copy constructor ~Llama() ; int size() ; void dump() ; void push(const T& data) ; T pop() ; void dup() ; // (top) A B C D -> A A B C D void swap() ; // (top) A B C D -> B A C D void rot() ; // (top) A B C D -> C A B D T peek(int offset) const ; // overloaded assignment operator // const Llama<T,LN_SIZE>& operator=(const Llama<T,LN_SIZE>& rhs) ; // // Add your public member functions & public data mebers here: // private: // // Add your private member functions & private data mebers here: // } ; #include "Llama.cpp" #endif

The implementation of the LlamaNode class is trivial and has been done for you. (See LlamaNode.cpp.) In fact, you are not allowed to change the LlamaNode files in any way.

Your assignment is to implement the member functions of the Llama class. You will need to add data members and member functions to the Llama class, but the rest of the header file should not be changed. You must implement the following member functions:

Finally, you must write a main() function that should be placed in a file named Driver.cpp. Your main() function should thoroughly test your implementation.

Files and Sample Runs

Here are some files for you to download:

The following three test programs may be used to check the compatibility of your implementation. These programs do not check the correctness of your implementation. Even if your implementation compiles and runs correctly with these programs, it does not mean your implementation is error-free. Grading will be done using programs that exercise your implementation much more thoroughly. You must do the testing yourself --- testing is part of programming. Conversely, if your implementation does not compile or does not run correctly with these test programs, then it is unlikely that it will compile or run correctly with the grading programs.

Implementation Notes

What to Submit

You must submit the following files to the proj1/src directory.

You do not need to submit LlamaNode.h and LlamaNode.cpp because those files should not have changed. If you do happen to place a copy of LlamaNode.h and/or LlamaNode.cpp in your submission directory, they will be replaced by a copy of the original version.

If you followed the instructions in the Project Submission page to set up your directories, you can submit your code using these Unix commands.

mkdir ~/cs341proj/proj1/src cp Llama.h Llama.cpp Driver.cpp ~/cs341proj/proj1/src/

Note: you only have to use mkdir to make the src subdirectory once. If you submit again, just use the cp command.

Discussion Topics

Here are some topics to think about: