diff --git a/pretext/LinearLinked/UnorderedListClass.ptx b/pretext/LinearLinked/UnorderedListClass.ptx
index 2940b91a..6b04970e 100644
--- a/pretext/LinearLinked/UnorderedListClass.ptx
+++ b/pretext/LinearLinked/UnorderedListClass.ptx
@@ -6,8 +6,8 @@
pointers. As long as we know where to find the first node (containing the first
item), each item after that can be found by successively following the
next links. With this in mind, the UnorderedList class must maintain
- a reference to the first node. shows the
- constructor. Note that each list object will maintain a single reference
+ a pointer to the first node. shows the
+ constructor. Note that each list object will maintain a single pointer
to the head of the list.
@@ -23,14 +23,14 @@ class UnorderedList {
};
-
Initially when we construct a list, there are no items. The assignment and declaration statment
+
Initially when we construct a list, there are no items. The assignment and declaration statement
UnorderedList myList;
creates the linked list representation shown in .
- As we discussed in the Node class, the special reference nullptr will again be used to state
+ As we discussed in the Node class, the special value nullptr will again be used to state
that the head of the list does not refer to anything. Eventually, the example list given earlier
will be represented by a linked list as shown in .
The head of the list points to the first node which contains the first item of the list.
- In turn, that node holds a reference to the next node (the next item) and so on.
+ In turn, that node holds a pointer to the next node (the next item) and so on.
It is very important to note that the list class itself does not contain any node objects.
Instead it contains a single pointer to only the first node in the linked structure.
@@ -76,13 +76,13 @@ class UnorderedList {
(*node).link.
The isEmpty method, shown in , simply checks to
- see if the head of the list is a reference to nullptr. The result of
+ see if the head of the list is nullptr. The result of
the boolean expression this->head==nullptr will only be true if there
are no nodes in the linked list. Since a new list is empty, the
constructor and the check for empty must be consistent with one another.
- This shows the advantage to using the reference nullptr to denote the
- end of the linked structure. Two references are equal if they both refer to the same
- object. We will use this often in our remaining methods.
+ This shows the advantage to using nullptr to denote the
+ end of the linked structure. Two pointers are equal if they hold the same address, that is,
+ point to the same node. We will use this often in our remaining methods.
UnorderedListisEmpty() method
@@ -131,7 +131,7 @@ mylist.add(54);
must reside in a node object. Line 2 creates a new node and places the
item as its data. Now we must complete the process by linking the new
node into the existing structure. This requires two steps as shown in
- . Step 1 (line 3) changes the next reference
+ . Step 1 (line 3) changes the next pointer
of the new node to refer to the old first node of the linked list. Now that the
rest of the linked list has been properly attached to the new node, we can
modify the head of the linked list to refer to the new node. The assignment
@@ -169,7 +169,7 @@ void add(int item) {
happens if the order of line 3 and line 4 is reversed? If the
modification of the head of the linked list happens first, the result can be
seen in . Since the head was the only external
- reference to the linked list nodes, all of the original nodes are lost and can
+ pointer to the linked list nodes, all of the original nodes are lost and can
no longer be accessed.
@@ -178,8 +178,8 @@ void add(int item) {
A diagram showing an incorrect process for adding a new node ("26") to a linked list, resulting in the loss of the original head node.
- The "head" pointer is updated to reference the new node ("26") without first preserving a reference to the original head node ("93").
- The original linked list, consisting of nodes "93," "17," "77," and "31," becomes disconnected, with no external reference to it,
+ The "head" pointer is updated to reference the new node ("26") without first preserving a pointer to the original head node ("93").
+ The original linked list, consisting of nodes "93," "17," "77," and "31," becomes disconnected, with no external pointer to it,
as indicated by the dashed oval labeled "no external reference."
The new node ("26") points to the end of the list, represented by a vertical line, making it the only accessible node in the structure.
The "temp" variable temporarily holds the new node during this process.
@@ -192,20 +192,20 @@ void add(int item) {
The next methods that we will implement size, search, and
remove are all based on a technique known as
linked list traversal. Traversal refers to the process of
- systematically visiting each node. To do this we use an external reference
+ systematically visiting each node. To do this we use an external pointer
that starts at the first node in the linked list. As we visit each node,
- we move the reference to the next node by traversing the next reference.
+ we move the pointer to the next node by traversing the next pointer.
To implement the size method, we need to traverse the linked list
and keep a count of the number of nodes that occurred.
shows the C++ code for counting the number of
- nodes in the linked list. The external reference is called current and is
+ nodes in the linked list. The external pointer is called current and is
initialized to the head of the linked list in line2. At the start of the
process we have not seen any nodes so the count is set to 0.
Lines 46 actually implement the traversal. As long as the current
- reference has not seen the end of the linked list (nullptr), we move current
+ pointer has not seen the end of the linked list (nullptr), we move current
along to the next node via the assignment statement in line 6. Again,
- the ability to compare a reference to nullptr is very useful. Every
+ the ability to compare a pointer to nullptr is very useful. Every
time current moves to a new node, we add 1 to count.
Finally, count gets returned after the iteration stops.
shows this process as it proceeds down the linked list.
@@ -282,9 +282,8 @@ bool search(int item) {
Since 17 is in the linked list, the traversal process needs to move only to the
- node containing 17. At that point, the variable found is set to
- true and the while condition will fail, leading to the return
- value seen above. This process can be seen in .
+ node containing 17. At that point, search returns true
+ immediately; there is no need to look at the rest of the list. This process can be seen in .
Searching a Linked List.
@@ -306,13 +305,13 @@ bool search(int item) {
traverse the linked list looking for the item we want to remove. Once we find
the item (recall that we assume it is present), we must remove it. The
first step is very similar to search. Starting with an external
- reference set to the head of the linked list, we traverse the links until we
+ pointer set to the head of the linked list, we traverse the links until we
discover the item we are looking for. Since we assume that item is
present, we know that the iteration will stop before current gets to
nullptr. This means that we can simply use the boolean found in the
condition.
-
When found becomes true, current will be a reference to the
+
When found becomes true, current will be a pointer to the
node containing the item to be removed. But how do we remove it? One
possibility would be to replace the value of the item with some marker
that suggests that the item is no longer present. The problem with this
@@ -327,16 +326,16 @@ bool search(int item) {
would like to make the change, it is too late to make the necessary
modification.
-
The solution to this dilemma is to use two external references as we
+
The solution to this dilemma is to use two external pointers as we
traverse down the linked list. current will behave just as it did
- before, marking the current location of the traverse. The new reference,
+ before, marking the current location of the traverse. The new pointer,
which we will call previous, will always travel one node behind
current. That way, when current stops at the node to be removed,
previous will be referring to the proper place in the linked list
for the modification.
shows the complete remove method. Lines 23
- assign initial values to the two references. Note that current
+ assign initial values to the two pointers. Note that current
starts out at the linked list head as in the other traversal examples.
previous, however, is assumed to always travel one node behind
current. For this reason, previous starts out with a value of
@@ -364,6 +363,7 @@ void remove(int item) {
} else {
previous->setNext(current->getNext());
}
+ delete current;
}
@@ -422,7 +422,7 @@ void remove(int item) {
in the linked list, then current will reference the first node in the
linked list. This also means that previous will be nullptr. We said
earlier that previous would be referring to the node whose next
- reference needs to be modified in order to complete the remove. In this
+ pointer needs to be modified in order to complete the remove. In this
case, it is not previous but rather the head of the linked list that needs
to be changed (see ).
@@ -433,7 +433,7 @@ void remove(int item) {
A diagram showing the actual removal of the node with the value "17" from the middle of a linked list.
The "current" pointer is positioned on the node with the value "17," which is to be removed, while the "previous" pointer points to the preceding node
- ("93"). The "next" reference of the "previous" node is updated to skip the "current" node and point directly to the node after ("77").
+ ("93"). The "next" pointer of the "previous" node is updated to skip the "current" node and point directly to the node after ("77").
This effectively removes the "17" node from the linked list while maintaining the proper structure of the list.
@@ -457,21 +457,63 @@ void remove(int item) {
-
Line 12 allows us to check whether we are dealing with the special case
+
Line 13 allows us to check whether we are dealing with the special case
described above. If previous did not move, it will still have the
value nullptr when the boolean found becomes true. In that case
- (line 13) the head of the linked list is modified to refer to the node after
+ (line 14) the head of the linked list is modified to refer to the node after
the current node, in effect removing the first node from the linked
list. However, if previous is not nullptr, the node to be removed is
somewhere down the linked list structure. In this case the previous
- reference is providing us with the node whose next reference must be
- changed. Line 15 uses the setNext method from previous to
+ pointer is providing us with the node whose next pointer must be
+ changed. Line 16 uses the setNext method from previous to
accomplish the removal. Note that in both cases the destination of the
- reference change is current.getNext(). One question that often
+ pointer change is current->getNext(). One question that often
arises is whether the two cases shown here will also handle the
situation where the item to be removed is in the last node of the linked
list. We leave that for you to consider.
+
Unlinking the node removes it from the list, but the node itself still
+ exists. It was created with new when the item was added, and it
+ stays on the heap until something calls delete on it. Once it is
+ unlinked, current is the only pointer left to it, and
+ current goes away when remove returns. If we did nothing,
+ the node could never be reached or freed again, which is a
+ memory leak. Line 18 gives the node back with
+ delete current;. It comes after the relinking, because we still
+ need current->getNext() to repair the list.
+
+
The same problem shows up when a whole list goes away. When
+ mylist goes out of scope, C++ frees the UnorderedList
+ object, which is just the head pointer. The nodes it points to
+ are not freed. To free them, we give the class a
+ destructor, shown in .
+ A destructor has the class's name with a ~ in front, takes no
+ arguments, and runs automatically when the object is destroyed. This one
+ traverses the list and deletes each node. Note the order of lines 4 and
+ 5: we must save current->getNext() in next before
+ deleting current, because once a node is deleted we are not
+ allowed to look inside it.
Because the list now deletes its nodes, copying an UnorderedList
+ is unsafe: the copy would share the same nodes, and both destructors
+ would try to delete them. In this chapter we never copy a list (notice
+ that the output operator below takes its list by const
+ reference), so we leave that problem aside for now.
+
You can try out the UnorderedList class in .
@@ -494,7 +536,7 @@ class Node {
public:
Node(int initdata) {
- data = initdata; //the initialized data is set as the head
+ data = initdata; //store the item in this node
next = nullptr; //the next node is set to nullptr, as there is no next node yet.
}
@@ -524,6 +566,15 @@ class UnorderedList {
head = nullptr;
}
+ ~UnorderedList() { // deletes every node when the list is destroyed
+ Node *current = head;
+ while (current != nullptr) {
+ Node *next = current->getNext();
+ delete current;
+ current = next;
+ }
+ }
+
bool isEmpty() { // the head node is empty if it is nullptr
return head == nullptr;
}
@@ -582,6 +633,7 @@ class UnorderedList {
} else {
previous->setNext(current->getNext());
}
+ delete current;
}
friend ostream& operator<<(ostream& os, const UnorderedList& ol);