(Latest Revision: 10/01/2000)
Lecture Notes on Data Structures and C++
- Virtues of abstract data typing and modular code.
- We use data structures to
implement the important "nouns"
in the statement of a programming
problem.
- Things like lists, queues, and
diagrams become data structures
in the program.
- For example a program that
schedules processes waiting to
use a CPU might employ a first-in,
first-out queue data structure
to represent the set of waiting
processes.
- Important "verbs" in the program
statement become operations on
the data.
- For example consider the idea
of processes leaving the wait
queue to run in the CPU and processes
returning to the queue to resume
waiting. That would be implemented
as the "QueueInsert" and "QueueDelete"
operations of the queue class.
- We must carefully design our data
structures so that they offer a set of
operations to the client that allows the
client to accomplish all that is needed
in the way of examination and
modification of the data. For example,
in the case of the queue the client can:
- check to see if the queue is
empty or full,
- peek at the front element of
a non-empty queue,
- create or destroy a queue
variable, and
- do insertions and deletions.
- The operations offered should
form a basis for all needed actions.
The client should be able to
do anything it needs to do to
the queue by executing some combination
of the basic operations provided.
- If we discover there is some
operation that clients need to
do but cannot do, we must add
another operation, or choose
a different data structure.
- The operations must be carefully
crafted and tested so that we can be
very confident that they work
correctly under all circumstances.
- At the same time, we must design the
operations so they do not allow the
client to corrupt the internal
consistency of the data structure.
- For example, suppose the
queue is implemented using pointer
variables front
and rear to keep
track of the locations of the
front and rear elements of the
queue.
- In this situation, no queue
operation available to the client
would allow that client to make
arbitrary changes to front
or rear.
- If the client had the ability
to change the value of front
or rear arbitrarily,
the client might make an error
that corrupted the queue data,
making it unusable.
- Of course, a designated
client operation such as "QueueInsert"
would have the power to make
changes to front and
rear. A client calling
"QueueInsert" would be making
changes to front and
rear indirectly, and
not in an arbitrary manner.
There would be a guarantee that
"QueueInsert" makes only the
appropriate changes to the pointers,
in a manner that keeps the representation
of the queue consistent.
- We place the code that implements each
data structure (the data declarations
and the definitions of the operations)
together in some particular place in the
program. Code in all other parts of the
program is forbidden to use the data
structure in any way other than by
making a call to one of the operations
intended to be used to access the data
structure.
- Take an example different
from the queue class for once:
if we implement something called
a diagram in our program
by making it a data structure,
then we should put all the variable
declarations and function definitions
that define this data structure
in one confined location in the
program. Code in all other parts
of the program is allowed only
to manipulate a diagram by using
one of the operations intended
for clients. If you wanted to
write code in some other part
of the program to create a diagram
and add some line segments and
circles to it, you would have
to call special functions designed
for that purpose -- functions
defined to be operations on the
diagram data structure.
- When we use data structures in the
manner described above, there are several
benefits:
- Programs are easier to design
correctly.
- That's because the interfaces
of the data structures
are clearly defined and
perform all needed functions.
- The parts of the program
are clearly understood.
- The program is formed
by fitting together the
parts in a simple way.
- It is easier to change the
way data structures are implemented.
- All we have to do is
go to the one confined
location where the code
for the operations is
located and make changes
there.
- If we merely change the
manner in which tasks
are done within the body
of those functions then
the rest of the program
will not have to be changed
in any way. The interface
the client sees will
not have changed.
- A client program that
uses one implementation
of a data structure can
easily be modified to
use a different implementation
simply by re-compiling.
- The integrity of the data is
protected from the clients.
- Code in the program is easier
to reuse because of its clear
interface to the rest of the
program.
- C++ classes make good vehicles for
creating data structures.
- Modularity: The rules for how
we declare C++ classes encourage
us to put all the code in a confined
area of the program, as we should.
- Information Hiding: Rules of
C++ (private variables and functions)
enable the programmer to really
restrict the client code to make
it impossible to access the data
structure except by using the
intended operations.