|
1
|
|
|
2
|
- Data Abstraction
- Given what we talked about last time, we need to step through an
example of an abstract data type using
- classes,
- constructors,
- member functions,
- data hiding,
- distinguishing the difference between what the client can do and what
the ADT can do
|
|
3
|
- Let’s begin by building a “list” abstraction
- Remember, the data structure used should be able to “plug and play”
(i.e., be replaced without affecting the client program
- Operations will incloude to:
- insert, remove, retrieve, and display
- create, destroy
|
|
4
|
- Once the operations are understood, we can begin to examine the data
- Let’s build a list to be used for a student roster
- so, the data will include the student name, current grade % in the
class, and psu id#
- we can use a struct or a class to represent the underlying data
- we will examine both of these approaches today
|
|
5
|
- The list of students can be implemented using a variety of data
structures
- Our choices are:
- array (statically allocated),
- array (dynamically allocated),
- linear linked list
- circular linked list
- doubly linked list
|
|
6
|
- struct data {
- char * name;
- char * psu_id;
- float grade;
- };
- Options for placement:
- before the class in the header file
- in the implementation file
- nested within the class
|
|
7
|
- struct data {
- char * name;
- char * psu_id;
- float grade;
- };
- class list {
- public:
- ...
|
|
8
|
- //.h file
- struct data;
- class list {
- public:
- ...
- //.cpp file
- struct data {
- char * name;
- char * psu_id;
- float grade;
- };
|
|
9
|
- class list {
- public:
- ...
- private:
- struct data {
- char * name;
- char * psu_id;
- float grade;
- };
- };
|
|
10
|
- class data {
- char * name;
- char * psu_id;
- float grade;
- friend class list;
- };
- If a class had been used, the members would have been private by default
requiring the list class to be declared as a friend...
|
|
11
|
- My personal preference is to use structures for the underlying data
- this has the benefit of grouping various data types together
- the data is accessible directly by the class that uses the underlying
data
- and there is little overhead to access the data members (of the name,
psu id#, and grade)
|
|
12
|
- But, doesn’t this violate data hiding?
no!
- think about what code can access this data? You would have to have the
‘array’ of students, or the ‘head’ pointer to the first node containing
a student to actually access the data
- certainly, the client can “see” the structure, but they can’t access
the data if properly hidden within the class!
|
|
13
|
- But, couldn’t I use a class instead and specify the members as public to
avoid using friends? yes...but...
- then what is the difference between a class and a struct?
- personally, I consider that structs should be used when you simply want
to group different data items and that classes be used when you want to
create new data types, new abstractions, or are doing OOP
|
|
14
|
- class list {
- public:
- list();
- ~list();
- int insert(const data &);
- int retrieve(char *, data &);
- int display();
- int remove (char *);
|
|
15
|
- private:
- data s_array[SIZE];
- int number_of_students;
- };
- This provides for a statically allocated array of students
- Notice, none of the member functions uses an index or passes the array
- Why do we need the 2nd data member?
|
|
16
|
- private:
- data * d_array;
- int number_of_students;
- int size_of_array;
- };
- This provides for a dynamically allocated array of students
- Notice, none of the member functions changes in their client interface
- Add: list (int size);
|
|
17
|
- By replacing the constructor with:
- list (int size=SIZE); //in
prototype only
- We can have only one constructor act as either the default constructor
(with the same size of an array as the previous example), or with a
client-specified size
- The constructor would need to:
- d_array = new data [size];
- size_of_array = size;
|
|
18
|
- private:
- node * head;
- };
- This provides for a linked list (linear, circular, or doubly linked)
- Notice, none of the member functions changes in their client interface
- That’s right. Insert and retrieve have the same arguments as before!!!!
|
|
19
|
- But, what does the node look like?
- struct node { //linear/circular
- data student;
- node * next;
- };
- Add, node * previous to the structure for a doubly linked list.
- Where do we place this?
- Should the “student” member be a pointer to a data instead?
|
|
20
|
- The purpose of the constructor is to initialize the data members
- So, for the dynamic array it simply allocated the array’s memory and
initialized the size. What else?
- Yes, it also needs to set the number of items currently stored in the
list to zero.
- Why doesn’t it need to initialize each element of the array?
|
|
21
|
- For the linked list implementation, the constructor would simply set the
head pointer to null
- In some situations, you will also want a second list data member, called
tail, to keep track of the end of the list
- This is important when you want to frequently add to the end of the list
- Why doesn’t a tail pointer help when removing the last item?
|
|
22
|
- The purpose of a destructor is to deallocate all dynamic memory and
close down any resources being used
- For the statically allocated array, the destructor had no purpose
- For the dynamically allocated array, it would be: delete [] d_array;
- Question: Do we need to set the other data members to zero? And, d_array
to zero?
|
|
23
|
- For a linked list, the purpose of the destructor is to deallocate all
memory
- This requires traversing through the linked data structure, deallocating
all items
- Will this do it?
- delete head;
- Of course not. This will deallocate one node unless the “node” has a
destructor of its own...
|
|
24
|
- OK, so what is wrong with this:
- while (head) {
- delete head;
- head = head->next;
- }
- delete head;
- Two things. We are accessing memory within the loop that has already
been deallocated
- Second, we have an extra “delete”
|
|
25
|
- OK, is this correct?
- while (head) {
- node * temp = head;
- delete head;
- head = temp; }
- Close...but why reallocate the memory for temp each time through the
loop
- Think about the inefficiency of this!!!
- Therefore, remove the “underlined” portion
|
|
26
|
- For insert, we would need to define what it is that insert actually does
- Does it insert at the beginning, at the end, in sorted order?
- The approach should be well documented in the header file for any client
program using the software
- Remember, insert should not prompt or read from the user but rather get
its data through arguments from the client
|
|
27
|
- Let’s examine some various prototype statements for insert and discuss
the pros/cons of each:
- int insert(const data &);
- bool insert(const data &);
- void insert(const data &);
- void insert(data &, bool);
- int insert(const node &);
|
|
28
|
- What about retrieve?
- Should it display the matching item or “return” it to the client
program?
- Which prototype is best?
- int retrieve(char *, data &);
- data retrieve(char *, bool);
- data & retrieve (char *, bool);
|
|
29
|
- Now that we have seen a partial example of a list abstraction, we should
examine the efficiency of using an array versus a linked list for the
data structure
- An array allows for direct access, which is only useful if you know the
position of where the data is located
- If the array is sorted, then a binary search can be used to get to the
correct position
|
|
30
|
- If a linked list is used, we must traverse to the correct spot to insert
into sorted order
- If the array is not sorted, and we are inserting at the beginning or the
end, an array is extremely efficient...direct access for both arrays and
linked lists why?
- Would you need a tail pointer now????
|
|
31
|
- But, what about retrieval?
- An array, if sorted, will allow us to use the binary search
- A linked list - regardless of if it is sorted or not - will require
traversal starting at the head
- Think of the overhead if you have a list of 10,000 items? How many
comparisons would we have in the worst case?
|
|
32
|
- With an array that is not sorted, the same is true. An array provides no
additional advantage in this case!
- Ok, so what about memory efficiency?
- An array’s size must be determined prior to actually needing to use the
memory
- A statically allocated array must be decided upon at compile time
- A dynamically allocated array’s size can be determined later, at run
time
|
|
33
|
- Now that we have seen a simple list example, and started to examine how
we can use different data structures to solve the same problem
- We will move on to examine linked list, circular linked list, linked
list of linked lists, doubly linked lists, etc. beginning next time!
- Our next ADT will be an “ordered list” adt!
|
|
34
|
- Programming Assignment Discussion
|