|
1
|
|
|
2
|
- Abstract Data Types
- Introduction to...Object Models
- Introduction to...Data Abstraction
- Using Data Abstraction in C++ ...an introduction to the class
- Members of a Class
- The class interface, using the class, the class interface versus
implementation
- Classes versus Structures
- Constructors, Destructors
- Dynamic Memory and Linked Lists
|
|
3
|
- The most important aspect of C++ is its ability to support many
different programming paradigms
- procedural abstraction
- modular abstraction
- data abstraction
- object oriented programming (this is discussed later, once we learn
about the concept of inheritance)
|
|
4
|
- This is where you build a “fence” around program segments, preventing
some parts of the program from “seeing” how tasks are being
accomplished.
- Any use of globals causes side effects that may not be predictable,
reducing the viability of procedural abstraction
|
|
5
|
- With modular abstraction, we build a “screen” surrounding the internal
structure of our program prohibiting programmers from accessing the data
except through specified functions.
- Many times data structures (e.g., structures) common to a module are
placed in a header files along with prototypes (allows external
references)
|
|
6
|
- The corresponding functions that manipulate the data are then placed in
an implementation file.
- Modules (files) can be compiled separately, allowing users access only
to the object (.o) files
- We progress one small step toward OOP by thinking about the actions that
need to take place on data...
|
|
7
|
- We implement modular abstraction by separating out various
functions/structures/classes into multiple .c and .h files.
- .c files contain the implementation of our functions
- .h files contain the prototypes, class and structure definitions.
|
|
8
|
- We then include the .h files in modules that need access to the
prototypes, structures, or class declarations:
- #include “myfile.h”
- (Notice the double quotes!)
- We then compile programs (on UNIX) by:
- CC main.c myfile.c
- (Notice no .h file is listed on the above line)
|
|
9
|
- Data Abstraction is one of the most powerful programming paradigms
- It allows us to create our own user defined data types (using the class
construct) and
- then define variables (i.e., objects) of those new data types.
|
|
10
|
- With data abstraction we think about what operations can be performed on
a particular type of data and not how it does it
- Here we are one step closer to object oriented programming
|
|
11
|
- Data abstraction is used as a tool to increase the modularity of a
program
- It is used to build walls between a program and its data structures
- what is a data structure?
- talk about some examples of data structures
- We use it to build new abstract data types
|
|
12
|
- An abstract data type (ADT) is a data type that we create
- consists of data and operations that can be performed on that data
- Think about a char type
- it consists of 1 byte of memory and operations such as assignment,
input, output, arithmetic operations can be performed on the data
|
|
13
|
- An abstract data type is any type you want to add to the language over
and above the fundamental types
- For example, you might want to add a new type called: list
- which maintains a list of data
- the data structure might be an array of structures
- operations might be to add to, remove, display all, display some items
in the list
|
|
14
|
- Once defined, we can create lists without worrying about how the data is
stored
- We “hide” the data structure used for the data within the data type --
so it is transparent to the program using the data type
- We call the program using this new data type: the client program (or
client)
|
|
15
|
- Once we have defined what data and operations make sense for a new data
type, we can define them using the class construct in C++
- Once you have defined a class, you can create as many instances of that
class as you want
- Each “instance” of the class is considered to be an “object” (variable)
|
|
16
|
- Think of a class as similar to a data type
- and an object as a variable
- And, just as we can have zero or more variables of any data type...
- we can have zero or more objects of a class!
- Then, we can perform operations on an object in the same way that we can
access members of a struct...
|
|
17
|
- Remember, we used a structure to group different types of data together
under a common name
- With a class, we can go the next step an actually define a new data type
- In reality, structures and classes are 100% the same except for the
default conditions
- everything you can do with a class you can do with a structure!
|
|
18
|
- First, let’s talk about some terminology
- Think of a class as the same as a data type
- Think of an object as the same as a variable
- An “object” is an instance of a class
- Just like a “variable” is an instance of a specific data type
- We can zero or more variables (or objects) in our programs
|
|
19
|
- I recommend using structures when you want to group different types of
data together
- and, to use a class when we are interested in building a new type of
data into the language itself
- to do this, I always recommend forming that data type such that it
behaves in a consistently to how the fundamental data types work
|
|
20
|
- We’ve been working with fundamental data types this term, such as ints,
floats, chars...
- Whenever we define variables of these types,
- memory is allocated to hold the data
- a set of operations can now be performed on that data
- different data types have different sets of operations that make sense
(the mod operator doesn’t make sense for floats...)
|
|
21
|
- Therefore, when we define a new data type with the class construct
- we need to specify how much memory should be set aside for each
variable (or object) of this type
- and, we need to specify which operations make sense for this type of
data (and then implement them!!)
- and, what operators makes sense (do be discussed with operator
overloading)
|
|
22
|
- Once we have decided on how the new type of data should behave, we are
ready to define a class:
- class data_type_name {
- public:
- //operations go here
- private:
- //memory is reserved here
- };
|
|
23
|
- class string {
- public:
- string();
- int copy(char []);
- int length();
- int display();
- private:
- char str[20];
- int len;
- };
|
|
24
|
- string::string() {
- str[0]=‘\0’; len = 0;
- }
- int string::copy(char s []) [
- if (strlen(s) < 20)
- strcpy (str, s);
- else {
- for (int i = 0; i< 20; ++i)
- str[i] = s[i];
- str[20]=‘\0’;
- len = strlen(str); return len; }
|
|
25
|
- int string::length() {
- return len;
- }
- int string::display() {
- cout <<str;
- return len;
- }
|
|
26
|
- Notice how similar defining objects of class is to defining variables of
any data type:
- string my_str; vs. int i;
- Defining an object causes the “constructor” to be invoked; a constructor
is the same named function as the class (string) and is used to
initialize the memory set aside for this object
- Think about how much memory is set aside?
- What initial values should it take on?
|
|
27
|
- Think about how you can use those objects
- my_str.copy(“hi!”);
- cout << my_str.length();
- We are limited to using only those operations that are defined within
the public section of the class interface
- The only “built-in” operation that can be used with objects of a class
is the assignment operation, which does a memberwise copy (as we learned
with structures)
|
|
28
|
- Notice how similar the use of these operations is to the cin.get
function.....
- cin.get(ch);
- This should be a clue. cin therefore is an object of the istream class.
- The dot is the member access operator; it allows us to access a
particular public member function defined within the istream class.
- The function get is therefore defined within the public section of the
istream class
|
|
29
|
- But, there are limitations!
- If our goal is to really be able to use my string objects in a way
consistent with the fundamental data types,
- then I would expect to be able to read strings using the extraction
operator
- and to display strings by directly using the insertion operator
- and to concatenate strings using +
|
|
30
|
- With the class as it is defined, none of these things can be done...
- the only operations that can be performed are those specified within
the public section of the class interface, and a memberwise copy with
the assignment operator
- No other operations are known
- Therefore, to be consistent, we must revise our class to use operator
overloading
|
|
31
|
- class string {
- public:
- string();
- int length();
- friend ofstream & operator <<
- (ofstream &, const string &);
- friend ifstream & operator >>
- (ifstream &, string &);
- private:
- char str[20];
- int len;
- };
|
|
32
|
- For a list of videos, we might start with a struct defining what a video
is:
- struct video {
- char title[100];
- char category[5];
- int quantity;
- };
|
|
33
|
- For a list of videos data type:
- class list {
- public:
- list();
- int add (const video
&);
- int remove (char title[]);
- int display_all();
- private:
- video
my_list[CONST_SIZE]; //for
now...
- int num_of_videos;
- };
|
|
34
|
- For a client to create a list object:
- main() {
- list home_videos; //has an array of 100 videos
- list kids_shows; //another 100 videos here...
- •••
- video out_of_site;
- cin.get(out_of_site.title,100,’\n’);
- cin.ignore(100,’\n’);
- •••
- home_videos.add(out_of_site); //use operation
|
|
35
|
- Data Hiding
- and
- Member Functions
|
|
36
|
- Terminology
- Data Hiding
- Class Constructors
- Defining and using functions in classes
- Where to place the class interface and implementation of the member
functions
|
|
37
|
- Class
- Object
- instance of a class, e.g., variable
- Members
- like structures, the data and functions declared in a class
- called “data members” and “member functions”
|
|
38
|
- A class could be a list, a string, a counter, a clock, a bank account,
etc.
- discuss a simple counter class on the board
- An object is as real as a variable, and gets allocated and deallocated
just like variables
- discuss the similarities of:
|
|
39
|
- For the list of videos data type we used
- class list { <--- the data type!!!
- public:
- list(); <--- the
constructor
- int add (const video
&); 3 member functions
- int remove (char title[]);
- int display_all();
- private:
- video
my_list[CONST_SIZE]; data
members
- int num_of_videos;
- }; <--- notice like structures we need a semicolon
|
|
40
|
- If we examine the previous class,
- notice that classes are really very similar to structures
- a class is simply a generalized structure
- in fact, even though we may not have used structures in this way...
- Structures and Classes are 100% identical except for their default
conditions...
- by default, all members in a structure are available for use by
clients (e.g., main programs); they are public
|
|
41
|
- We have seen the use of structures in a more simple context,
- as we examined with the video struct.
- It had three members (data members)
- called title, category, and quantity.
- They are “public” by default,
- so all functions that have objects of type video can directly access
members by:
- video object;
- object.title object.category object.quantity
|
|
42
|
- This limited use of a structure was appropriate, because
- it served the purpose of grouping different types of data together as a
single unit
- so, anytime we want to access a particular video -- we get all of the
information pertaining to the video all at once
|
|
43
|
- Remember, anything you can do in a struct you can do in a class.
- It is up to your personal style how many structures versus classes you
use to solve a problem.
- Benefit: Using structures for simple “groupings” is compatible with C
- struct video {
- char title[100];
- char category[5];
- int quantity;
- };
|
|
44
|
- To accomplish data hiding and encapsulation
- we usually turn towards classes
- What is data hiding?
- It is the ability to protect data from unauthorized use
- Notice, with the video structure, any code that has an object of the
structure can access or modify the title or other members
|
|
45
|
- With data hiding
- accessing the data is restricted to authorized functions
- “clients” (e.g., main program) can’t muck with the data directly
- this is done by placing the data members in the private section
- and, placing member functions to access & modify that data in the
public section
|
|
46
|
- So, the public section
- includes the data and operations that are visible, accessible, and
useable by all of the clients that have objects of this class
- this means that the information in the public section is “transparent”;
therefore, all of the data and operations are accessible outside the
scope of this class
- by default, nothing in a class is public!
|
|
47
|
- The private section
- includes the data and operations that are not visible to any other
class or client
- this means that the information in the private section is “opaque” and
therefore is inaccessible outside the scope of this class
- the client has no direct access to the data and must use the public
member functions
- this is where you should place all data to ensure the memory’s
integrity
|
|
48
|
- The good news is that
- member functions defined in the public section can use, return, or
modify the contents of any of the data members, directly
- it is best to assume that member functions are the only way to work
with private data
- (there are “friends” but don’t use them this term)
- Think of the member functions and private data as working together as a
team
|
|
49
|
- Let’s see how “display_all” can access the data members:
- class list {
- public: notice it is public
- int display_all() {
- for (int i=0; i<num_of_videos; ++i)
- cout
<<my_list[i].title <<‘\t’
- <<my_list[i].category
- <<‘\t’ <<my_list[i].quantity <<endl;
- }
- •••
- private:
- video my_list[CONST_SIZE];
- int num_of_videos;
- };
|
|
50
|
- Notice, that the display_all function can access the private my_list and
num_of_videos members, directly
- without an object in front of them!!!
- this is because the client calls the display_all function through an
object
- so the object is implicitly available once we enter “class scope”
|
|
51
|
- In reality, the previous example was misleading. We don’t place the
implementation of functions with this this class interface
- Instead, we place them in the class implementation, and separate this
into its own file
|
|
52
|
- Class Interface: list.h
- class list {
- public:
- int display_all()
- •••
- private:
- video my_list[CONST_SIZE];
- int num_of_videos;
- };
- list.h can contain:
- prototype statements
- structure declarations and definitions
- class interfaces and class declarations
- include other files
|
|
53
|
- Class Implementation list.c
- #include “list.h” notice the double quotes
- int list::display_all() {
- for (int i=0; i<num_of_videos; ++i)
- cout
<<my_list[i].title <<‘\t’
- <<my_list[i].category
- <<‘\t’ <<my_list[i].quantity <<endl;
- }
- Notice, the code is the same
- But, the function is prefaced with the class name and the scope
resolution operator!
- This places the function in class scope even though it is implemented
in another file
- Including the list.h file is a “must”
|
|
54
|
- Remember that when you define a local variable in C++, the memory is not
automatically initialized for you
- This could be a problem with classes and objects
- If we define an object of our list class, we really need the
“num_of_videos” data member to have the value zero
- Uninitialized just wouldn’t work!
|
|
55
|
- Luckily, with a constructor we can write a function to initialize our
data members
- and have it implicitly be invoked whenever a client creates an object
of the class
- The constructor is a strange function, as it has the same name as the
class, and no return type (at all...not even void).
|
|
56
|
- The list constructor was:
(list.h)
- class list {
- public:
- list(); <--- the
constructor
- •••
- };
- The implementation is: (list.c)
- list::list(){
- num_of_videos = 0;
- }
|
|
57
|
- The constructor is implicitly invoked when an object of the class is
formed:
- int main() {
- list fun_videos; implicitly calls the constructor
- list all_videos[10]; implicitly calls the
- constructor 10 times for
- each of the 10 objects!!
|
|
58
|
- But, what if we didn’t want to waste memory for the title (100
characters may be way too big (Big, with Tom Hanks)
- So, let’s change our video structure to include a dynamically allocated
array:
- struct video {
- char * title;
- char category[5];
- int quantity;
- };
|
|
59
|
- Let’s write a class that now allocates this list of videos dynamically,
at run time
- This way, we can wait until we run our program to find out how much
memory should be allocated for our video array
|
|
60
|
- What changes in this case are the data members:
- class list {
- public:
- list();
- int add (const video
&);
- int remove (char title[]);
- int display_all();
- private:
- video *my_list;
- int video_list_size;
- int num_of_videos;
- };
|
|
61
|
- Now, let’s think about the implementation.
- First, what should the constructor do?
- initialize the data members
- list::list() {
- my_list = NULL;
- video_list_size = 0;
- num_of_videos = 0;
- }
|
|
62
|
- Remember function overloading? We can have the same named function occur
(in the same scope) if the argument lists are unique.
- So, we can have another constructor take in a value as an argument of
the number of videos
- and go ahead and allocate the memory,
so that subsequent functions can use the array
|
|
63
|
- list::list(int size) {
- my_list = new video [size];
- video_list_size = size;
- num_of_videos = 0;
- }
- Notice, unlike arrays of characters, we don’t need to add one for the
terminating nul!
|
|
64
|
- The client can cause this 2nd constructor to be invoked by defining
objects with initial values
- list fun_videos(20); //size is
20
- If a size isn’t supplied, then no memory is allocated and nothing can
be stored in the array....
|
|
65
|
- To fix this problem, we can merge the two constructors and replace them
with a single constructor:
- list::list(int size=100) {
- my_list = new video [size];
- video_list_size = size;
- num_of_videos = 0;
- }
- (Remember, to change the prototype for the constructor in the class
interface)
|
|
66
|
- Then, we can deallocate the memory when the lifetime of a list object is
over
- When is that?
- Luckily, when the client’s object of the list class lifetime is over (at
the end of the block in which it is defined) -- the destructor is
implicitly invoked
|
|
67
|
- So, all we have to do is write a destructor to deallocate our dynamic
memory.
- list::~list() {
- delete [] my_list;
- my_list = NULL;
- •••
- }
- (Notice the ~ in front of the function name)
- (It can take NO arguments and has NO return type)
- (This too must be in the class interface....)
|
|
68
|
- What is the difference between a class and a struct
- What is a data member?
- Where should a data member be placed in a class? (what section)
- What is a member function?
- Where should member functions be placed, if clients should use them?
|
|
69
|
- What is the difference between a member function and a regular-old C++
function?
- What is the purpose of the constructor?
- Why is it important to implement a constructor?
- What is the difference between a class and an object?
|
|
70
|
- Show an example of how a client program defines an object of a list
class
- How then would the client program call the constructor? (trick
question!)
- How then would the client program call the display_all function?
- Why are parens needed?
|
|
71
|
- Write a simple class interface (called number) that has the following
members:
- an integer private data member (containing a value)
- a constructor
- a set member function, that takes an integer as an argument and returns
nothing
- a display member function
|
|
72
|
- Now, let’s try our hand at implementing these functions
- a constructor
- a set member function, that takes an integer as an argument and returns
nothing
- a display member function
|
|
73
|
- What happens if we forgot to put the keyword public in the previous
class interface?
- Why is it necessary to place the class name, followed by the scope
resolution operator (::) when we implement a member function outside of
a class?
|