You are on page 1of 5

230 Chapter 5 / Linked Lists

When both a const and a non-const version of a function are present, the com-
piler automatically chooses the correct version, depending on whether the func-
tion was activated by a constant node (such as const node *c ) or by an
ordinary node.

When to Provide Both Const and Non-Const Versions


of a Member Function
When the return value of a member function is a pointer to a
node, you should generally have two versions: a const
version that returns a const node* , and an ordinary
version that returns an ordinary pointer to a node.

 P I T FALL
DEREFERENCING THE NULL POINTER
One of the most common pointer errors is writing the expression *p or p-> when
the value of the pointer p is the null pointer. This must always be avoided because
the null pointer does not point to anything. Therefore, when p is the null pointer, *p
(which means “the thing that p points to”) is meaningless. In this case, p-> is also
meaningless. Because the asterisk in *p is called the dereferencing operator, we
can state this rule: Never dereference the null pointer.
Accidental dereferencing of the null pointer is sometimes a hard error to track
down. The error does not cause a syntax error. Instead, when the program is
running, there will be an attempt to interpret the null pointer as if it were a valid
address. Sometimes this causes an immediate run-time error with a message such
as “Address protection violation” or “Bus error, core dumped.” But on other
machines, the null address might be a valid address, causing your program to read
or write an unintended memory location. Often this memory location is part of the
machine’s operating system, resulting in part of the operating system being cor-
rupted. At some later point, perhaps after your program has completed, the cor-
rupted operating system can cause an error. Fortunately, restarting your machine
usually writes a fresh copy of the operating system into memory—but even so,
never dereference the null pointer!

Self-Test Exercises for Section 5.1


1. Write the class definition needed for a node in a linked list. Use the name
value_type for the type of the data.
2. What is the meaning of the C++ constant NULL? What additional code is
needed in a program in order to use NULL?
3. Describe two common uses for the null pointer.
A Linked-List Toolkit 231

4. What value does the default constructor of value_type give if the


value_type is one of the built-in number or bool types?
5. What is the data type of head_ptr in Figure 5.3 on page 224? What is
the data type of *head_ptr? What is the data type of head_ptr->data( )?
6. Suppose that head_ptr points to the first node of a non-empty linked
list. Write code to print the word “zero” if the data in the first node is 0.
7. Consider this statement: cout << (*head_ptr).data( );. What would
happen if the parentheses around head_ptr were omitted? Write the
alternative syntax to activate the data( ) class member.
8. Suppose that b_ptr is a pointer to a bag (from Chapter 3). One of the
bag’s member functions is size, which returns the number of elements
in a bag. Write a statement that prints the number of elements in the bag
pointed to by b_ptr. Use the member selection operator.
9. Describe a problem that can occur if you dereference the null pointer.
10. Why is this link implementation wrong?
node* link( ) const { return link_field; }
11. What is the solution for the problem in the previous exercise?
12. Why is it okay to have just a single const version of the node’s data
member function (without a second non-const version)?

5.2 A LINKED-LIST TOOLKIT

We’re now in a position to design container classes that use linked lists to store
their items. The member functions of the container class will put things into the
linked list and take them out. This use of a linked list is similar to our previous
use of an array in a container class. However, you may find that storing and
retrieving items from a linked list is more work than using an array because we
don’t have the handy indexing mechanism (such as data[i]) to read or write
elements. Instead, the class requires extra functions just to build and manipulate
the lists—parts that are not central to the container’s main objectives. a collection of
In fact, many container classes might need these same extra functions, which linked-list
suggests that we should write a collection of linked-list functions once and for functions
all, allowing any programmer to use the functions in the implementation of a
container class. This is what we will do, creating a small toolkit of fundamental
linked-list functions. The primary purpose of the toolkit is to allow a container
class to store elements in a linked list with a simplicity and clarity that is similar
to using an array. In addition, having the functions written and thoroughly tested
once will allow us to use the functions to implement many different container
classes with high confidence in their reliability.
258 Chapter 5 / Linked Lists

Self-Test Exercises for Section 5.2


13. Suppose you want to use a linked list where the items are strings from
the Standard Library string class. How would you need to change the
node1.h header file?
14. Write the pattern for a loop that traverses the nodes of a linked list.
15. When should a node pointer be a value parameter in a function’s param-
eter list?
16. Suppose that locate_ptr is a pointer to a node in a linked list (and it is
not the null pointer). Write a statement that will make locate_ptr move
to the next node in the list. What does your statement do if locate_ptr was
already pointing to the last node in the list?
17. Suppose that head_ptr is a head pointer for a linked list of numbers.
Write a few lines of code that will insert the number 42 as the second
item of the list. (If the list was originally empty, then 42 should be added
as the first node instead of the second.)
18. Write a statement to correctly set the tail pointer of a list when a new first
node has been added to the list. Assume that head_ptr points to the new
first node.
19. Which of the toolkit functions use new to allocate a new node? Which
use delete to return a node to the heap?
20. What is the general rule to follow when using the delete operator with
node pointers?
21. Suppose that head_ptr is a head pointer for a linked list of numbers.
Write a few lines of code that will remove the second item of the list. (If
the list originally had only one item, then remove that item instead; if it
had no items, then leave the list empty.)
22. Suppose that head_ptr is a head pointer for a linked list with just one
node. What will head_ptr be after list_head_remove(head_ptr)?
23. Rewrite the list_insert with just one line of code in the implementation.
24. Implement this function:
void list_piece(
const node* start_ptr, const node* end_ptr,
node*& head_ptr, node*& tail_ptr
)
// Precondition: start_ptr and end_ptr are pointers to nodes on the same
// linked list, with the start_ptr node at or before the end_ptr node.
// Postcondition: head_ptr and tail_ptr are the head and tail pointers
// for a new list that contains the items from start_ptr up to but not
// including end_ptr. The end_ptr may also be NULL, in which case the
// new list contains elements from start_ptr to the end of the list.
25. Implement two versions of the list_locate function (one where the
parameter is node* and a second version using const node* ).
Chapter Summary 283

CHAPTER SUMMARY
• A linked list consists of nodes; each node contains some data and a
pointer to the next node in the list. The pointer field of the final node con-
tains the null pointer.
• Typically, a linked list is accessed through a head pointer that points to
the head node (i.e., the first node). Sometimes a linked list is accessed
elsewhere, such as through the tail pointer that points to the last node.
• You should be familiar with our functions to manipulate a linked list.
These functions follow basic patterns that every programmer uses. Such
functions are not node member functions (so that they can handle empty
lists with no nodes).
• Linked lists can be used to implement a class. Such a class has one or
more private member variables that are pointers to nodes in a linked list.
The member functions of the class use the linked-list functions to manip-
ulate the linked list, which is accessed through private member variables.
• You have seen two classes implemented with the linked-list toolkit: a bag
and a list. You will see more in the chapters that follow.
• A doubly linked list has nodes with two pointers: one to the next node and
one to the previous node. Doubly linked lists are a good choice for sup-
porting a cursor that moves forward and backward.
• Classes can often be implemented in many different ways, such as by
using a dynamic array or using a linked list. In general, arrays are better at
random access; linked lists are better at insertions/removals at a cursor. In
the STL, the vector uses a dynamic array and the list uses a linked list. A
third choice, the STL deque, uses a mechanism that we’ll see later.

Solutions to Self-Test Exercises


SOLUTIONS TO SELF-TEST EXERCISES ?
1. See the class definition on page 226. 5. head_ptr is a pointer to a node. On the other
hand, *head_ptr is a node, and the type of
2. The null pointer is a special value that can be head_ptr->data( ) is node::value_type.
used for any pointer that does not point any-
where. The cstdlib library should be 6. if (head_ptr->data( ) == 0)
included to use NULL, but because NULL is not cout << "zero";
part of the std namespace, it can be written 7. The operation of accessing a data member has
without a preceding std::. higher precedence than the dereferencing
3. The null pointer is used for the link field of the asterisk. Therefore, head_ptr.data( ) will
final node of a linked list; it is also used for cause a syntax error because the call to data( )
the head and tail pointers of a list that doesn’t is attempted before deferencing head_ptr.
yet have any nodes. The alternative syntax is head_ptr-
>data( );
4. Numbers are given a default value of 0, and
bools are given a default value of false. 8. cout << b_ptr->size( );
284 Chapter 5 / Linked Lists

9. The portions of the operating system that are you implement it from Exercise 24), and
currently in memory can be overwritten. list_head_insert. The delete operator is
used in the functions list_head_remove,
10. The implementation will compile correctly. list_remove, and list_clear.
But since the return value is a pointer to a
node in the list, a programmer could use the 20. Never call delete unless you are actually
return value to change the linked list. In gen- reducing the number of nodes.
eral, the return value from a constant member
function should never allow the underlying 21. Using functions from Section 5.2:
if (head_ptr != NULL)
linked list to be changed.
{
11. Change the return type to const node*, and if (head_ptr->link() == NULL)
provide a second non-const function that list_head_remove(head_ptr);
returns the link as an ordinary node*. else
list_remove(head_ptr);
12. We need only one function to access the data }
field because this function returns a copy of
the data (and it is not possible to change the 22. It will be the null pointer.
underlying linked list by having merely a copy
of the data from a node). 23. The one line will be:
previous_ptr->set_link
13. The #include <string> directive must be (new node
added to the other include directives in the (entry, previous_ptr->link())
toolkit’s header file. Then we can change the );
typedef statement to:
24. The implementation is nearly the same as
typedef string value_type;
list_copy, but the copying must stop when
14. for ( the end node has been copied.
cursor = head_ptr;
cursor != NULL; 25. Here is the const version:
cursor = cursor -> link( ) const node* list_locate(
) const node* head_ptr,
{...} size_t position
)
15. A node pointer should be a value parameter // Library facilities used: cassert, cstdlib
when the function accesses and possibly mod- {
ifies a linked list, but does not need to make const node *cursor;
the pointer point to a new node. size_t i;
16. locate_ptr = locate_ptr->link( ); assert(0 < position);
If locate_ptr is already pointing to the last cursor = head_ptr;
node before this assignment statement, then for (
the assignment will set locate_ptr to the i = 1;
null pointer. (i < position)
&&
17. Using functions from Section 5.2: (cursor != NULL);
if (head_ptr == NULL) ++i
list_head_insert(head_ptr, 42); )
else cursor = cursor->link();
list_insert(head_ptr, 42);
return cursor;
18. if (head_ptr->link( )==NULL) }
tail_ptr = head_ptr;
26. The definition makes bag::value_type the
19. The new operator is used in the functions same as node::value_type, so that a pro-
list_insert, list_copy, list_piece (if grammer can use bag::value_type without

You might also like