Presentation is loading. Please wait.

Presentation is loading. Please wait.

Linked Stacks and Queues

Similar presentations


Presentation on theme: "Linked Stacks and Queues"— Presentation transcript:

1 Linked Stacks and Queues
Chapter 4

2 Objectives Introduce dynamic memory management.
Pointers Dynamic variables Memory leaks/garbage Dynamic arrays new and delete keywords Introduce linked lists. Node class Linked stack implementation Linked queue implementation Introduce object oriented features. Destructors Copy constructors Operator overloading

3 Homework Overview Written (max 20 points) Programming (max 5 points)
4.1 E1 (2 pts) 4.1 E2 (a,b,c) (3 pts each) 4.2 E1 (3 pts) 4.2 E2 (b) (3 pts) 4.3 E2 (10 pts) 4.4 E4 (5 pts) Programming (max 5 points) 4.2 E2 (a) (5 pts) Group Project (38 points) 4.4 E1, E2, E3, P1, P2 (38 pts)

4 Dynamic Structures What if we don’t know how big to make a stack or queue. We may need to grow and shrink the structure as necessary. There are two approaches: Use contiguous memory (array) and copy to a larger array if it gets full. Use pointers and distributed memory to allocate or free small chunks of memory as necessary.

5 Pointers A pointer is a variable that stores the memory address of some other object. A pointer to a float: More than one pointer can contain the same address. Two pointers to the same string:

6 Invalid Pointers An invalid pointer is dangerous.
It is like a loaded gun pointed in a random direction. If you use it you might not hit anything important – but you might shoot yourself in the foot! To avoid this we have a special NULL value we use for invalid pointers.

7 Orphaned Memory We need also to be careful to maintain some way to access the memory we have allocated. If we have memory without a valid pointer then it is lost until the program terminates. We cannot access the information stored. We cannot free the memory for other uses. This situation is called a memory leak and the orphaned memory is call garbage.

8 Linked List Linked List – a list of records where each record contains a pointer to the next item. The last item contains a NULL pointer. The entries are not necessarily stored near each other in memory. This list is not contiguous.

9 Automatic vs. Dynamic Objects
Automatic objects have the following properties: Created and destroyed automatically by the compiler. Have variable names. Must be known (name, size and type) at compile time. Dynamic objects have the following properties: Not determined at compile time. Created and destroyed as needed as the program runs. Not assigned variable names. Must be managed by the programmer. Accessed using pointers. Still have a size and a type.

10 Pointers and Automatic Variables
Pointers can be associated with both automatic and dynamic variables. Using a pointer with an automatic object can be confusing. We can already access the variable using its name, why do we need a pointer? In general avoid using pointers with automatic variables.

11 Declaring Pointers Pointers are declared using an asterisk (*).
Item *item_ptr; Here item_ptr is a pointer to an object of type Item. read from right to left. Notice the pointer has a type which indicates the size of the object it points to and how it can be used. The type may be a type or a class. White space can be before or after the asterisk. Item* item_ptr;

12 Creating Dynamic Objects
We use the new command to create dynamic objects. Item *p; p = NULL; p = new Item; Notice that until we assign it a value we do not know what is contained in the memory we have allocated.

13 Out of Memory In the unfortunate case where there is not enough memory available, an exception is thrown. The book assumes NULL is returned. Unless we catch the exception the program will crash. This can be changed using (nothrow). p = new(nothrow) Item;

14 Dereferencing a Pointer
We access the memory associated with a pointer by dereferencing it using an asterisk. *p = 1234; cout << *p; We free the memory associated with a pointer using the delete command. delete p;

15 Uninitialized Pointers
A common error is forgetting to allocate memory. int *p; *p = 23; We don’t know where p pointed, so we don’t know where we put the 23. Possible outcomes: We used memory that is protected (illegal to use) – program crashes immediately. We used memory that is being used by something important – program may crash immediately, program may crash sometime later, program may run with unexpected results. We may use memory that is currently unused but that will be allocated and used in the future – program may crash sometime later, program may run with unexpected results. We may use memory that is OK – program runs fine. The behavior may change from one run of the program to the next. This is a very hard bug to find.

16 Pointers and Arrays We can use new and delete to manage contiguous blocks (arrays). This line allocates memory for an array of 23 integers. array = new int[23]; We can access the entries using indexing as usual. array[5] = 19; This uses the fact that an array is really a pointer. We can free the memory using delete. delete []item_array; This is the legal way to create an array with a size that is not known at compile time.

17 Assignment and Pointers
Suppose p and q are both pointers to strings. How are p=q and *p=*q different? Consider p=q: One of the strings is orphaned and memory is lost. An change dereferencing either pointer will also change the data dereferenced by the other.

18 Assignment and Pointers
Consider *p = *q: We have two copies of the same string. Changes to the value dereferenced by one pointer will leave the other value unchanged.

19 Pointers to Objects There is a little syntactic sugar to help when using pointers to objects. Example: class Fraction{ public: int numerator; int denominator; }; Fraction *p; p = new Fraction; (*p).numerator = 5; p->denominator = 2; We can either use either (*p). or p-> to dereference the pointer and access a field or a method.

20 Nodes in Linked Lists A linked list is made up of nodes that each contain a data entry and a pointer to the next entry (node) in the list. Because everything will be public and we will only have constructor methods, we will use a struct instead of a class. struct Node{ Node_entry entry; Node *next; Node(); Node(Node_entry item, Node *link = NULL); }; Note the circular definition. The definition of a node refers to a pointer to a node. This is legal in C++. Note that the constructor is overloaded – there are two constructors with the same name but different signatures.

21 Creating Nodes We can create a node in three different ways.
If we do not want to assign values, use the first constructor: Node my_node; If we want to assign a data value but not a pointer value, use the second constructor with one parameter: Node my_node(‘a’); If we want to assign a data value and a pointer value, use the second constructor with two parameters: Node my_node(‘b’, p);

22 Creating Unnamed Nodes
We can also create nodes that are accessed through pointer, not names. Node *front; front = new Node(‘a’); front = new Node(‘b’, front);

23 Node Constructors Node::Node() /* Post: an empty node is created that has no next node. */ { next = NULL; } Node::Node(Node_entry item, Node *link) /* Post: a node is created that contains the entry item and has the node link as the next node. */ entry = item; next = link;

24 Homework – Section 4.1 (page 126)
Written E1 (written on paper) (2 pts) E2 (a, b, c) (written on paper, this does not need to be part of a running program) (3 pts each)

25 Linked Stacks To make things easier we will use a typedef to link the stack entries and the node entries. typedef Stack_entry Node_entry; All the action is a stack takes place at the top, so we only need to keep a pointer to the top of the stack.

26 Stack Class Definition
class Stack { public: Stack(); bool empty() const; Error_code pop(); Error_code top(Stack_entry &item) const; Error_code push(const Stack_entry &item); protected: Node *top_node; };

27 Stack Methods: Constructor
Construct empty stack: Stack s; Stack::Stack() // Constructor /* Post: The Stack is initialized to be empty.*/ { top_node = NULL; }

28 Stack Methods: Push We have two cases to consider when adding (push) an item to the stack. Add a new node to an empty stack: s.push(item1); Add a new node to a nonempty stack: s.push(item2);

29 Stack Methods: Push Error_code Stack::push(const Stack_entry &item)
/* Post: If the Stack is not full, item is added to the top of the Stack. If the Stack is full, an Error_code of overflow is returned and the Stack is left unchanged. */ { Node *new_top = new(nothrow) Node(item, top_node); if (new_top == NULL) return overflow; top_node = new_top; return success; }

30 Stack Methods: Pop Pop an item off of a stack: s.pop();
Error_code Stack::pop() /* Post: If the Stack is not empty, the top of the Stack is removed. If the Stack is empty, an Error_code of underflow is returned.*/ { Node *old_top = top_node; if (top_node == NULL) return underflow; top_node = old_top->next; delete old_top; return success; }

31 Stack Methods: Top and Empty
Top and Empty are easy: Error_code Stack::top(Stack_entry &item) const /* Post: If the Stack is not empty, the top of the Stack is returned in item. If the stack is empty an Error_code of underflow is returned. */ { Error_code outcome = success; if (top_node == NULL) return underflow; item = top_node->entry; return outcome; } bool Stack::empty() const /* Post: If the Stack is empty, true is returned. Otherwise false is returned. */ return (top_node == NULL);

32 Homework – Section 4.2 (page 130)
Written E1 (written on paper) (3 pts) E2 (b) (written on paper) (3 pts) Programming E2 (a) ( code) (5 pts)

33 Additional Features There are some subtle problems with our Stack class. If someone lets a Stack go out of scope before emptying it there will be a memory leak. We allocated the memory, we need to deallocated it. If someone uses a line like stack1 = stack2 they may not get what they expect. If stack1 is not empty, we will have a memory leak. We would get two stack records but they would share the same linked list. It would be nice to be able to create a stack as a copy of an existing stack.

34 Stack Methods: Destructor
A destructor is a function that is automatically called when an object goes out of scope. The name of the destructor is the same as the name of the class but it is preceded by a tilde (~). Stack::~Stack() // Destructor /* Post: The Stack is cleared. */ { while (!empty()) pop(); } We will need to add this to our class definition.

35 Stack Methods: Destructor
Adding a destructor solves one problem, but is creates another. Suppose a Stack is passed to a function by value. The function makes a copy of the Stack object, but the underling linked list is not copied. The top_node pointers of both Stack objects point to the same memory location. When the function terminates, the destructor is called on the copy. This destroys not only the copy but also the original Stack.

36 Stack Methods: Copy Constructor
To solve the destructor problem and also allow us to create a stack as a copy of another, we will make a copy constructor. This function can be called when creating a stack. Stack s2(s1); It will also be automatically called when we pass a Stack by value. Passing a Stack by value might be expensive in time and memory, if it is large. Passing it as a constant reference might be better.

37 Stack Methods: Copy Constructor
Stack::Stack(const Stack &original) // copy constructor /* Post: The Stack is initialized as a copy of Stack original. */ { Node *new_copy, *original_node = original.top_node; if (original_node == NULL) top_node = NULL; else { // Duplicate the linked nodes top_node = new_copy = new(nothrow) Node(original_node->entry); while (original_node->next != NULL){ original_node = original_node->next; new_copy->next = new(nothrow) Node(original_node->entry); new_copy = new_copy->next; }

38 Stack Methods: Overloading =
So that assignment works they way we would expect we need to overload the assignment operator (=). If you think about it an operator is just a function. Operator notation: b=a; Function notation: b.operator = (a); Both of these do the same thing. Notice the operator “belongs” to the argument on the left. This is so that a chain like a=b=c will work. Notice the following about the = operator It modifies the argument on the left. For chaining to work it must return the original value assigned, so that the next = will have something to work with.

39 Stack Methods: Overloading =
What our overloaded = operator will need to do. Copy the data in the calling parameter (original) – the one on the right. Clear out the data, if any, of the parameter on the left. This is to prevent a memory leak. Make the left parameter point to the new copy. Return a reference (pointer) to the new left parameter.

40 Stack Methods: Overloading =
const Stack& Stack::operator = (const Stack &original) // Overload assignment operator /* Post: The Stack is reset as a copy of Stack original. */ { Node *new_top, *new_copy, *original_node = original.top_node; if (original_node == NULL) new_top = NULL; else { // Duplicate the linked nodes new_copy = new_top = new(nothrow) Node(original_node->entry); while (original_node->next != NULL){ original_node = original_node->next; new_copy->next = new(nothrow) Node(original_node->entry); new_copy = new_copy->next; } while (!empty()) // Clear out old Stack entries pop(); top_node = new_top; // and replace them with new entries. return *this;

41 Stack Methods: Overloading =
The ordering seems a little odd here. Why do it in this order? Make a copy Empty left hand argument Make left hand equal copy The answer is that the user might do something silly (but legal) like the line x = x; If we empty the left hand argument first, there won’t be anything to copy!

42 Homework – Section 4.3 (page 137)
Written E2 (written on paper, try replacing the operator = function in the Stack class with your new versions and make sure it works.) (10 pts)

43 Linked Queues Things happen at both the front and the rear of a queue.
We will need two pointers. The many of the queue operations are similar to the stack operations.

44 Linked Queue Methods Serving a Queue is similar to popping a Stack.
Appending a Queue is different, however.

45 Linked Queue Methods Like a Stack, a Queue will require:
a destructor. a copy constructor. an assignment operator. We can extend queues in a manner similar to that in chapter 3. Be careful calculating the size.

46 Linked Queue Class class Queue { public: Queue(); bool empty() const; Error_code append(const Queue_entry &item); Error_code serve(); Error_code retrieve(Queue_entry &item) const; ~Queue(); const Queue& operator = (const Queue &original) Queue(const Queue &original); protected: Node *front, *rear; };

47 Queue Methods: Append Error_code Queue::append(const Queue_entry &item) /* Post: item is added to the rear of the Queue. If the Queue is full return an Error_code overflow and leave the Queue unchanged. */ { Node *new_rear = new(nothrow) Node(item); if (new_rear == NULL) return overflow; if (rear == NULL) front = rear = new_rear; else { rear->next = new_rear; rear = new_rear; } return success;

48 Queue Methods: Serve Error_code Queue::serve() /* Post: The front of the Queue is removed. If the Queue is empty return an Error_code underflow. */ { if (front == NULL) return underflow; Node *old_front = front; front = old_front->next; if (front == NULL) rear = NULL; delete old_front; return success; }

49 Queue Methods: Retrieve
Error_code Queue::retrieve(Queue_entry &item) const /* Post: The front fo the Queue retrieved to the output parameter item. If the Queue is empty return an Error_code of underflow. */ { if (front == NULL) return underflow; item = front->entry; return success; }

50 Homework – Section 4.4 (page 140)
Written E4 (written on paper) (5 pts) Group Project E1, E2, E3, P1, P2 ( code) (33 pts)


Download ppt "Linked Stacks and Queues"

Similar presentations


Ads by Google