more on functions we can declare a function to have more than one signature but the same name. this...
TRANSCRIPT
More on functions
We can declare a function to have more than one signature but the same name.
This is called overloading, some languages refer to this as polymorphism
See Overload1.cpp Set as exercise:
//write a program that overloads the function Findroots(int, int, int)
FindRoots(double, double, double) For finding the roots of a quadratic equation.
Overloading functions
The compiler creates two such functions which are different even though they have the same name.
The compiler knows which function to invoke by the parameters that are being passed to it.
FindRoots(2.1,2,4,-4.5) will invoke the function expecting doubles.
PointersA variable in a program is something with a name, the value of which can vary. The way the compiler and linker handles this is that it assigns a specific block of memory within the computer to hold the value of that variable.
The size of that block depends on the range over which the variable is allowed to vary. For example, on 32 bit PCs the size of an integer variable is 4 bytes. On older 16 bit PCs integers were 2 bytes.
Pointers and ArraysWhat is a pointer? One of those things that people find difficult in C/C++ is the concept of pointers. The purpose of this power_point is to provide an introduction to pointers and their usage. One of the main reason why people have a problem with pointers is that they have not fully understood the concept of a variable, (as they are used in C++). Thus we start with a discussion of C++ variables in general.
PointersIn C/C++ the size of a variable type such as an integer need not be the same on all types of machines. Furthermore there is more than one type of integer variable in C++. We have integers, long integers and short integers which you can read up on in any text on C++. If you want to know the size of the various types of integers on your system, running the following code will give you that information.
Pointers
#include <iostream> using namespace std; int main()
{cout<<"size of a short is“<<sizeof(short)<< endl;cout<< "size of an int is “ <<sizeof(int) << endl;cout<< "size of a long is “<<sizeof(long) << endl;return 0;
} //see sizeof.cpp //long same size as int
Pointers When we declare a variable we inform the compiler of
two things, the name of the variable and the type of the variable. For example, we declare a variable of type integer with the name k by writing:
int k; On seeing the "int" part of this statement the compiler
sets aside 4 bytes of memory (on a PC) to hold the value of the integer.
It also sets up a symbol table. In that table it adds the symbol k and the relative address in memory where those 4 bytes were set aside.
Pointers Thus, later if we write: k = 2; we expect that, at run time when this statement is
executed, the value 2 will be placed in that memory location reserved for the storage of the value of k.
In C++ we refer to a variable such as the integer k as an "object".
In a sense there are two "values" associated with the object k.
One is the value of the integer stored there (2 in the above example) and the other the "value" of the memory location, i.e., the address of k.
Some texts refer to these two values with the nomenclature rvalue (right value, pronounced "are value") and lvalue (left value, pronounced "el value") respectively.
Pointers In some languages, the lvalue is the value
permitted on the left side of the assignment operator '=' (i.e. the address where the result of evaluation of the right side ends up). The rvalue is that which is on the right side of the assignment statement, the 2 above. rvalues cannot be used on the left side of the assignment statement. Thus: 2 = k; is illegal.
Actually, the above definition of "lvalue" is somewhat modified for C. According to K&R II (page 197): [1]
"An object is a named region of storage; an lvalue is an expression referring to an object."
Pointers
However, at this point, the definition originally cited above is sufficient.
Okay, now consider: int j, k; k = 2; j = 7; //line 1 k = j; //line 2
Pointers
int j, k, k=2,j=7; //line 1 k=j; //line 2 In the above, the compiler interprets the j in line 1 as
the address of the variable j (its lvalue) and creates code to copy the value 7 to that address.
In line 2, however, the j is interpreted as its rvalue (since it is on the right hand side of the assignment operator '=').
That is, here the j refers to the value stored at the memory location set aside for j, in this case 7.
So, the 7 is copied to the address designated by the lvalue of k.
Pointers
In all of these examples, we are using 4 byte integers so all copying of rvalues from one storage location to the other is done by copying 4 bytes.
Had we been using two byte integers, we would be copying 2 bytes.
Now, let us say that we have a reason for wanting a variable designed to hold an lvalue (an address).
Pointers
The size required to hold such a value depends on the system.
On older desk top computers with 64K of memory total, the address of any point in memory can be contained in 2 bytes.
Computers with more memory would require more bytes to hold an address.
The actual size required is not too important so long as we have a way of informing the compiler that what we want to store is an address.
Pointers Such a variable is called a pointer variable
(for reasons which hopefully will become clearer a little later).
In C++ when we define a pointer variable we do so by preceding its name with an asterisk *.
In C++ we also give our pointer a type which, in this case, refers to the type of data stored at the address we will be storing in our pointer.
For example, consider the variable declaration: int *ptr; //ptr will store the address of an //integer
Pointers
ptr is the name of our variable (just as k was the name of our integer variable).
The '*' informs the compiler that we want a pointer variable, i.e. to set aside however many bytes is required to store an address in memory.
The int says that we intend to use our pointer variable to store the address of an integer.
Such a pointer is said to "point to" an integer. However, note that when we wrote int k; we did not
give k a value
Pointer
If this definition is made outside of any function ANSI compliant compilers will initialize it to zero.
Similarly, ptr has no value, that is we have not stored an address in it in the above declaration.
In this case, again if the declaration is outside of any function, it is initialized to a value guaranteed in such a way that it is guaranteed to not point to any C++ object or function.
A pointer initialized in this manner is called a "null" pointer.
Pointer
The actual bit pattern used for a null pointer may or may not evaluate to zero since it depends on the specific system on which the code is developed.
To make the source code compatible between various compilers on various systems, we use a null pointer.
The null pointer goes under the name NULL.
Thus, setting the value of a pointer using the null pointer we use NULL, as with an assignment statement such as
Pointers
ptr = NULL guarantees that the pointer has
become a null pointer. Similarly, just as one can test for an
integer value of zero, as in if(k == 0), we can test for a null
pointer using
if (ptr == NULL). //note uppercase
Pointers
But, back to using our new variable ptr. Suppose now that we want to store in
ptr the address of our integer variable k. To do this we use the unary &(ampersand) operator and write:
ptr = &k; ptr k
Pointers What the & operator does is to retrieve the
lvalue (address) of k, even though k is on the right hand side of the assignment operator '=', and copies that to the contents of our pointer ptr.
Now, ptr is said to "point to" k. Now there is only one more operator we need
to discuss. The "dereferencing operator" is the asterisk
and it is used as follows: *ptr = 7;
Pointers
This will copy 7 to the address pointed to by ptr.
Thus if ptr "points to" (contains the address of) k, the above statement will set the value of k to 7.
That is, when we use the '*' this way we are referring to the value of that which ptr is pointing to, not the value of the pointer itself.
ptr
K
7
Pointers
Similarly, we could write: cout << *ptr; to print to the screen the integer value stored
at the address pointed to by ptr. Lets run a program UseofpointersinC++.cpp
Pointers and Array types
Okay, let us move on. Let us consider why we need to identify
the type of variable that a pointer points to, as in:
int *ptr; One reason for doing this is so that
later, once ptr "points to“ something, if we write:
*ptr = 2;
Pointers and array types
The compiler will know how many bytes to copy into that memory location pointed to by ptr.
If ptr was declared as pointing to an integer, 4 bytes would be copied.
Similarly for floats and doubles the appropriate number will be copied.
More on pointers
But, defining the type that the pointer points to permits a number of other interesting ways a compiler can interpret code.
For example, consider a block in memory consisting of ten integers in a row.
That is, 40 bytes of memory are set aside to hold 10 integers.
Pointers and Array types
Now, let us say we point our integer pointer ptr at the first of these integers.
Furthermore lets say that integer is located at memory location 100 (decimal). What happens when we write:
ptr + 1; Because the compiler "knows" this is a pointer (i.e.
its value is an address) and that it points to an integer (its current address, 100, is the address of an integer)
Pointer and Array types it adds 4 to ptr instead of 1, so the pointer "points
to" the next integer, at memory location 104. Similarly, were the ptr declared as a pointer to a
short, it would add 2 to it instead of 1. The same goes for other data types such as floats,
doubles, or even user defined data types such as structures (which we discussed last week).
This is obviously not the same kind of "addition" that we normally think of.
In C++ it is referred to as addition using "pointer arithmetic", a term which we will come back to later.
Pointers
Similarly, since ptr++ is equivalent to ptr + 1, incrementing a pointer using
the unary ++ operator, increments the address it stores by the amount sizeof(type) where "type" is the type of the object pointed to. (e.g. 4 for an integer).
Pointers and Arrays
Since a block of 10 integers located contiguously (along a line, or next to) in memory is, by definition, an array of integers, this brings up an interesting relationship between arrays and pointers.
Consider the following: int my_array[ ] = {1,23,17,4,-5,100};
Pointers and Arrays
Here we have an array containing 6 integers. We refer to each of these integers by means
of a subscript to my_array, i.e. using my_array[0] through my_array[5].
So that my_array[0] = 1, my_array[5] = 100
But, we could alternatively access them via a pointer as follows:
int *ptr; ptr = &my_array[0]; /* point our pointer at the first */
//integer in our array
Pointers and arrays
And then we could print out our array either using the array notation or by dereferencing our pointer.
The following code illustrates this: Run arrays_pointers1.cpp Run arrays_pointers2.cpp
Pointers Arrays
Compile and run the above program and carefully note lines A and B and that the program prints out the same values in either case.
Also observe how we dereferenced our pointer in line B, i.e. we first added i to it and then dereferenced the new pointer.
Change line B to read:
Pointers and Arrays
cout << "ptr + “<< i <<“ *ptr++; and run it again... then change it to: cout << "ptr + i “ << *(++ptr); and try once more. Each time try and predict the outcome
and carefully look at the actual outcome.
Pointers and arrays
In C++, the standard states that wherever we might use &var_name[0] we can replace that with var_name, thus in our code where we wrote:
ptr = &my_array[0]; we can write: ptr = my_array; to achieve the same result. So the array name is the base address i.e.
address of first element in the array.
Pointers and Arrays
This leads many texts to state that the name of an array is a pointer.
Perhaps better to mentally think that "the name of the array is the address of first element in the array".
Many people (including myself) have a tendency to become confused by thinking of it as a pointer.
For example, while we can write ptr = my_array;
Pointers and arrays
we cannot write my_array = ptr; The reason is that while ptr is a variable,
my_array is a constant. That is, the location at which the first element
of my_array will be stored cannot be changed once my_array[] has been declared.
Earlier when discussing the term "lvalue" I cited K&R (Richie) - where it stated:
Pointers and arrays "An object is a named region of storage; an
lvalue is an expression referring to an object".
This raises an interesting problem. Since my_array is a named region of storage,
why is my_array in the above assignment statement not an lvalue?
To resolve this problem, some refer to my_array as an "unmodifiable lvalue".
Modify the example program above by changing
Pointers and arrays
ptr = &my_array[0]; to ptr = my_array;
Pointers and Arrays
Now, let us delve a little further into the difference between the names ptr and my_array as used above.
Some writers will refer to an array's name as a constant pointer.
What do we mean by that? Well, to understand the term "constant" in this sense, let us go back to our definition of the term "variable".
When we declare a variable we set aside a place in memory to hold the value of the appropriate type.
Pointers and Arrays
Once that is done the name of the variable can be interpreted in one of two ways.
When used on the left side of the assignment operator, the compiler interprets it as the memory location to which to move that value resulting from evaluation of the right side of the assignment operator.
But, when used on the right side of the assignment operator, the name of a variable is interpreted to mean the contents stored at that memory address set aside to hold the value of that variable.
Pointers and Arrays
With that in mind, let us now consider the simplest of constants, as in:
int i, k; i = 2; Here, while i is a variable and then occupies space
in the data portion of memory, 2 is a constant and, as such, instead of setting aside memory in the data segment, it is imbedded directly in the code segment of memory.
Pointers and Arrays
That is, while writing something like k = i; Tells the compiler to create code which at run
time will look at memory location &i to determine the value to be moved to k, code created by i = 2; simply puts the 2 in the code and there is no referencing of the data segment.
That is, both k and i are objects, but 2 is not an object.
Pointers and Arrays
This might be a good place explain further the use of the
(void *) expression which we can use. As we have seen we can have pointers of
various types
Pointers and Arrays
So far we have discussed pointers to integers and pointers to characters.
In coming lectures we will be learning about pointers to structures and perhaps pointer to pointers.
Also we have learned that on different systems the size of a pointer can vary.
As it turns out it is also possible that the size of a pointer can vary depending on the data type of the object to which it points.
Pointers and Arrays
Thus, as with integers where you can run into trouble attempting to assign a long integer to a variable of type short integer, you can run into trouble attempting to assign the values of pointers of various types to pointer variables of other types.
To minimize this problem, C++ provides for a pointer of type void.
We can declare such a pointer by writing:
Pointers and Arrays
void* vptr; A void pointer is sort of a generic pointer. For example, while C++ will not permit the
comparison of a pointer to type integer with a pointer to type character, for example, either of these can be compared to a void pointer.
Pointers and Strings
The study of strings is useful to further tie in the relationship between pointers and arrays.
It also makes it easy to illustrate how some of the standard C++ string functions can be implemented.
Finally it illustrates how and when pointers can and should be passed to functions.
Pointers and Strings In C++, strings are arrays of characters. This is not necessarily true in other languages. In BASIC, Pascal, Fortran and various other languages, a string
has its own data type. But in C++ it does not. In C++ a string is an array of characters terminated with a
binary zero character (written as '\0'). To start off our discussion we will write some code which, while
preferred for illustrative purposes, you would probably never write in an actual program. Consider, for example:
Pointers and Strings
char my_string[40];
my_string[0] = 'T';
my_string[1] = 'e';
my_string[2] = 'd':
my_string[3] = '\0';
Pointers and strings
While one would never build a string like this, the end result is a string in that it is an array of characters terminated with a nul character.
By definition, in C++, a string is an array of characters terminated with the nul character.
Be aware that "nul" is not the same as "NULL". The nul refers to a zero as defined by the escape sequence '\0'
Pointers and Strings
That is it occupies one byte of memory. NULL, on the other hand, is the name of the macro used to initialize null pointers. NULL is #defined in a header file in your C++ compiler, nul may not be #defined at all.
Since writing the above code would be very time consuming, C++ permits two alternate ways of achieving the same thing.
Pointers and Strings
First, one might write: char my_string[40] = {'T', 'e', 'd', '\
0',}; But this also takes more typing than is
convenient. So, C++ permits: char my_string[40] = "Ted";
Pointers and Strings
When the double quotes are used, instead of the single quotes as was done in the previous examples, the nul character ( '\0' ) is automatically appended to the end of the string.
In all of the above cases, the same thing happens.
The compiler sets aside an contiguous block of memory 40 bytes long to hold characters and initialized it such that the first 4 characters are Ted\0.
Run pointers3.cpp
More on strings
char* my_strcpy(char dest[], char source[]) { int i = 0; while (source[i] != '\0') { dest[i] = source[i]; i++; } dest[i] = '\0'; return dest; }
More on Strings
Recall that strings are arrays of characters. Here we have chosen to use array notation
instead of pointer notation to do the actual copying.
The results are the same, i.e. the string gets copied using this notation just as accurately as it did before.
This raises some interesting points which we will discuss.
More on strings
Since parameters are passed by value, in both the passing of a character pointer or the name of the array as above, what actually gets passed is the address of the first element of each array.
Thus, the numerical value of the parameter passed is the same whether we use a character pointer or an array name as a parameter.
This would tend to imply that somehow source[i] is the same as *(p+i).
More on Strings In fact, this is true, i.e wherever one writes a[i] it
can be replaced with *(a + i) without any problems. In fact, the compiler will create the same code in either case.
Thus we see that pointer arithmetic is the same thing as array indexing.
Either syntax produces the same result. This is NOT saying that pointers and arrays are the
same thing, they are not. We are only saying that to identify a given element of an
array we have the choice of two syntaxes, one using array indexing and the other using pointer arithmetic, which yield identical results
More on Strings
Now, looking at this last expression, part of it. (a + i), is a simple addition using the + operator and the rules of C/C++ state that such an expression is commutative.
That is (a + i) is identical to (i + a). Thus we could write *(i + a) just as easily as *(a + i).
More on Strings
But *(i + a) could have come from i[a] !
From all of this comes the curious truth that if:
char a[20]; int i; writing a[3] = 'x'; is the same as writing 3[a] = 'x';
More on Strings Try it! Set up an array of characters, integers
or longs, etc. and assign the 3rd or 4th element a value using the conventional approach and then print out that value to be sure you have that working.
Then reverse the array notation as I have done above.
A good compiler will not balk and the results will be identical. A curiosity... nothing more!
Now, looking at our function above, when we write:
More on strings
Another way to speed up the pointer version would be to change:
while (*source != '\0') to simply while (*source) since the value within
the parenthesis will go to zero (FALSE) at the same time in either case.
More on Strings
dest[i] = source[i]; due to the fact that array indexing and pointer arithmetic yield identical results, we can write this as:
*(dest + i) = *(source + i); But, this takes 2 additions for each value taken on
by i. Additions, generally speaking, take more time
than incrementations (such as those done using the ++ operator as in i++).
This may not be true in modern optimizing compilers, but one can never be sure. Thus, the pointer version may be a bit faster than the array version.
More on Strings At this point you might want to experiment
a bit with writing some of your own programs using pointers.
Manipulating strings is a good place to experiment. You might want to write your own versions of such standard functions as:
strlen(); strcat(); strchr(); and any others you might have on your system.
We will come back to strings and their manipulation through pointers in a future lectures
//see stringlength.cpp
Introduction to Classes
Object Oriented Programming
C++ Classes Class
A structured data type used to represent ADTs Class member – a component of a class
data member function (method)We can hide and bind different parts of the class structure
from other parts of the program. To do this we use the reserved words public, private or protected (we will not look at the protected interface in great detail at present).
This hiding is called encapsulation
Recall Structs
Separates implementation of data from operations.
Structs are said to be passive in the sense they just sit there and wait for something else to do something to them. Generally they do not have their own functions to
manipulate the data. Structs are heterogeneous data structures, can
you recall why?
Classes Are active in the sense that the data and operations and
bound together in one neat package. Data operates on itself and interacts with other data. A class has associated with it its own functions, these are
called methods in other languages e.g. Java There is a public and private interface. There is also a protected interface which we will not dwell on in great detail. There are also functions called friends
Class types Like a struct a class is a programmer defined type written by the
programmer for the programmer. It is not a data object of atomic type.
But it can have attributes (the data) of atomic type. You create or instantiate an object of a type, rather like creating a variable of atomic type
Think of the class type like a template the “builder” if you will. the objects are those things you build from the template. The
variables of that type. Just like we have int x; is a declaration of an int variable
with label x, we can declare a variable of type class e.g Time today, here the class name is Time and the object
name is today
class Time {public:void Set(int hours, int minutes, int
seconds) void Increment(); void print(); Time(int int Hours, int initMins,
int initSecs); Time(); private: int hours; int mins; int secs;};//See TimeClass2.cpp
//Look at displaydatestruct.cpp//and discuss
int main() {Time1 A;
return 0;}
instantiate object A of typeTime
The Constructor
Time::Time(){cout << "Setting the time" << endl;hour=0;
minute=0; second = 0;}//note the constructor has the same name as the
//class itself. Note also it does not have a return //type not even void
The constructor
Definition: The constructor is a member function of a class.
It is invoked every time an object is created. If no explicit constructors are defined, the
default constructor, which the compiler provides is used.
Class Fundamentals
A class defines a new data type, from which we can create objects (variables of this class).
When we define a class, we can define both the data and the code that operates on the data.
A class declaration is similar to the struct definition.
The structure of a class
class class-name { private data and functions access-specifier: data and functions access-specifier: data and functions ... ... ... access-specifier: data and functions } object-list;
An example of a class
class Employee { private: double wage; int ID;
public: void setID (int n); int getID (); void putwage (double w); double getwage(); };
The class
A class is a logical abstraction, but an object has physical existence.
access-specifier can be: public: Allow functions or data to be accessible to
other parts of the program. private: May be accessed only by other members
of the class. protected: Used when inheritance is involved;
discussed later.
New terms class is the keyword that begins a class declaration By default members of a class are private to it, so if
we do not use an interface (i.e. public, private or protected) the data become private, in a struct they default to public (this is the main difference between the two)
The public keyword is used to declare the public members of the class
Objects
An object is like a variable only more complex - it holds structured data and exhibits certain behaviour.
The data access can be restricted (or encapsulated). //new buzz word
The terms object and class are analogous to the concepts of variables and datatypes
Objects
Objects are instances of a class. The term instantiated is synonymous with
objects and classes. It simply means to create a variable of that
particular class
Classes.
Consider a Student class, which defines objects that have the following three properties (accessible data):
Registration number which is of type integer
Name of type string
Fees of type integer
The student class
We can create an instance of the Student class using either of the following syntaxes:
Student st(1234, "Mary Smith", 2420); Student st = Student(1234, "Mary Smith",
2420);
The default constructor We can also use the default constructor (if there is one
defined for the class) as follows to create an instance of Student that contains the default data:
Student st; Note that Student st(); is not valid code. It is possible to redefine the contents of an object using
an assignment statement. For example, the following statement changes the
contents of the already-existing object: st = Student(5678, "Bill Brown", 1320);
Encapsulation.
Not all the data stored in an object is accessible to the outside world.
Access is defined by the object's member functions which expose the data through the object's interface.
For example the Student class could have an accessor member function called get_number() that defines access to the student's ID number as follows:
st.get_number(); // returns the ID number of the
//Student object st
More on encapsulation
reg_no = st.get_number(); //assigns the ID number to the variable //reg_no. i.e. //places it in it To access the properties and member functions of
an object, use the dot operator as shown above. Objects can also have mutator member functions
that allow the properties to be modified. For example, you could use the Student class's
set_name() function to change the name property of a student:
Classes again
st.set_name("Betty Jones"); this would change the name of st from Bill
Brown to Betty Jones.
A closer look at Classes I
A typical function call looks like this: x = sqrt(y); A typical member function looks like this: t = s.addnumbers(a, b);
Classes I
We know how to use objects, and in this lesson we are going to learn how to create them.
Just as variables are made of a particular data type, for example char, int, double, so objects are instances of a class.
Classes I
In C++ classes either Come as standard to the language, for
example we have string objects or are created by programmers. Member functions (or methods) are defined
as part of the class definition.
A Basic Date class
We want our class to represent the day and the month not worry about leap years At the most basic level we could simply use
two int variables to represent the day and the month, but there would be no logical correlation between them.
We want the two numbers to be conceptually linked.
The Date class To declare a class, use the class keyword and a
name. By convention, class names begin with a capital
letter. //programmers decided this The class declaration follows inside curly braces,
and ends with a semicolon after the closing brace. The declaration defines the interface and the data
that the class will hold. An interface defines the access (read, write or none)
you want the outside world to have some access to an object's data and methods.
The Date class
The public interface comprises the data and methods you want to expose to the outside world; the private interface comprises the data and methods you want to encapsulate or hide from the outside world.
Encapsulated data and methods are by default accessible only to other objects of the same class.
You can also define friend functions, which we illustrate later.
The Date class class Date //note capitals { public: Date(); // constructor Date(int int); // this gives the option of changing // the Date void add_day (void); void display(void); private: int day; //these are the so-called attributes int month; };
The Date class Before we declare the class, we must decide what
we want to store in it and how we want to access the data:
It should hold a pair of integers representing the month and the day of the month
We want to be able to create an object and specify the date it represents
We want to be able to increment the date by one day
We want to be able to print the date.
The Date class
Firstly we declare the class by name, Date, and then declare the interface
The public interface The first thing we have to declare for any class is its
constructor. Without a constructor there is no way to instantiate
(create) an object. Normally an object has a so-called default
constructor, which creates a default instance of the class and initialises its data.
In this case the declaration takes the form Date(); This is the block of code that will be called by code
such as Date today; the date represented by the object it creates will be the default.
The Date class Next we define a constructor that allows us to create
a Date object and specify the date it represents: Date(int, int); This is the method called by code such
as today = Date(5, 3); The add_day() method will be used to add one day
to the date. It takes no arguments and returns nothing. This function will change the data and is known as a
mutator.
The Date class
The last method declared, display(), also takes no arguments and returns nothing.
It will be used to print the date. Since the method does not modify but only
accesses data it is known as an accessor function. We can add the keyword const to the declaration so
the compiler knows the function should not change the data fields.
Not done here.
The Date class: The private interface
Next we turn to the private part of the class declaration.
Here we declare the data fields and methods we want the class to encapsulate (hide).
We only have two data items, the day and the month, both integers.
Defining the class's methods We now have to define these functions. Since the identifier of a member function may be used in
several classes we have to explicitly identify the class whose member function we are defining. To do this, we use the :: notation. We begin by defining the default constructor.
Date::Date() //:: is the scope resolution operator { day = 1; month = 1; }
The class’s methods Next we define the add_day function void Date::add_day(void) { if (is it the last day of the month?) { day = 1; month = month % 12 + 1; //increment //the month } else day++;}
The Date class
This code is incomplete because we have no way yet of knowing which is the last day of the month.
We can therefore define a global, constant array of 13 integers, each element containing the number of days of the appropriate month.
Since January is month 1, we can define the first element of the array as zero.
The Date class
const int mdays[] = {0, 31, 28, 31, 30, 31, 30 , 31, 31 , 30, 31, 30, 31};
Similarly we can define an array to hold the names of the months:
const string mnames[] = {"", "Jan", "Feb", "Mar", "Apr","May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
The Date class We are now in a position to test for the last day of the
month and can amend the add_day() function as follows: void Date::add_day(void) { if (day == mdays[month]) // is it last day of the // month? { day = 1; month = month %12 + 1; } else day++; }
The Date class
The display() function is easily defined:
void Date::display(void) const
{
cout << day << " " << mnames[month] << endl;
}
The Date class
Finally we come to the second constructor function that allows us to specify the date.
We have to test the validity of the arguments supplied, which we can do either by including the <cassert> library and an assert statement or by writing some output to the cerr output stream and calling the exit function
(requires the <cstdlib> library)
The Date class Date::Date(int d, int m) { assert(m >= 1 && m <= 12 && d >= 1 && d <= mdays[m]); // assert requires the <cassert> library /* exit function as follows: if(m<1 || m>12) { cout << "Invalid month." << endl; exit(1); } */ day = d; month = m; }
The Date class All the declarations in the class have now been defined. We can create a main() function to test it: int main(void) { Date d1; Date d2(30, 11); d1.display(); d2.display(); d1.add_day(); d1.display(); } //see Date.cpp
The Queue class //this class implements a queueclass Queue{int q[100];int sloc, rloc; //start location and rear locationpublic:void init();void qput(int i);int qget();};
Classes continued
So a class contains both private and public members So q, sloc and rloc are private, this means that
they can be accessed only by other members of the queue class e.g. the functions of this class and NOT by other parts of the program in which this class has been written.
The functions are also referred to as methods This is known as ENCAPSULATION.
Classes continued Typically we place the functions in the public
interface and the data in the private interface, but we can declare private functions if we so wished.
The functions init(), qput() and qget() are called member functions of the class, note the parentheses to denote that they are functions.
Classes continued Keep in mind that an object forms a bond between the functions
and the data. A member function has access to the private parts of the class of
which it is a member. Thus init(), qput() and qget() have access to q, sloc and rloc. Once a class is declared we can create objects (variables) of the
type class as follows: Queue q1, q2 This will declare two objects q1 and q2 of type Queue
Declaration of classes
class class_name{
private: data and functions
public: public data and functions
} object_list;
//recall we can do this with structs i.e. the //object list part
Coding the functions
void queue::qput(int i){ if (sloc==100){ cout << “Queue is full ” << endl; return; }sloc++;q[sloc] = i;}//this will put the value i at position q[sloc] in the //array of
integers
:: The scope resolution operator
Note the use of the :: This is known as the scope resolution operator,
which means that the function name to the right of the two colons is in the scope of the class name to the left of the colon
In short it means that the function (or method) belongs to the class or the method is in the scope of the class.
The :: operator again
The :: is called the scope resolution operator. Essentially it tells the compiler that this version of qput belongs to the queue class
Or put differently :: states that qput() is in Queue’s scope
Member functions can only be invoked relative to an object. To call a member function from part of your program that is not part of the class one must use the object name and the dot operator, as we did with structs e.g.
Queue ob1,ob2; ob1.init();
The dot operator continued
The invocation ob1.init() causes init() to operate on ob1’s copy of the data.
Keep in mind that ob1 and ob2 are separate objects.
Now look at queue.cpp and discuss
A closer look at class member access
How to access class members is a cause of considerable confusion for programmers.
So for this reason we will look at this in more detail.
Look at myclass.cpp example and explain
Member function access continued Lets look at setab(). As is it a member function it can refer to a and b
directly without explicit reference to an object i.e. without the dot operator
Second note that a is private and b is public, so b can be accessed by code outside myclass. So main assigns a value of 20 to b
reset() is a function of myclass so has access to members of the class without the use of dot operator.
In this case it calls setab()
Constructors and Destructors
It is very common for some part of an object to require initialisation before it can be used.
Recall the queue class previously, before the queue could be used the variables rloc and sloc were set to zero. This was done using the function init().
Automatic initialisation is done using the constructor
Lets look at the queue2.cpp. (no constructor)
The use of a Constructor
queue::queue() //note no return type { rloc=0; sloc=0; cout << "Queue initialised " << endl; }Here the constructor writes to the screen Queue initialised, this is not
common. An objects constructor is called when the object is created
The Destructor
The complement to the constructor is the destructor.
In many cases an object will need to perform some action or series of actions when it is destroyed
Reasons for destructor: object may need to deallocate memory that it had previously allocated
The Destructor
In C++ the destructor is the function that handles deactivation. The destructor has the same name as the constructor and thus the class, but it is preceded by the tilda ~.
Like constructors they do not have a return type. The queue class does not require a destructor but we will include one for illustration, see queue3.cpp
Parameterised Constructors
A constructor function can have parameters thus we can have different signatures for the constructor.
This allows you to give member variables values on inception
Lets look at queue4.cpp Here we will see a constructor with more than one
signature, this is known as POLYMORPHISM analogous to OVERLOADING
Public and Private If you look closely at the previous definition of our classes
you may note one or two things class definition is similar to struct definition
In fact C++ treats structs as classes where by default all members are public
class definition contains function prototypes the operations are bound tightly to the data class functions know about class data so you do not need to
pass class data to functions as arguments
The Private and public interfaces
class definition has a line with a label public: this tells the compiler which parts of the class are visible to other things (clients) e.g. main()
class members are by default are private. That is private members functions and data are hidden to all.
private data and functions are only visible to an object itself.
Class Data Manipulation
The principle of Information Hiding is the key for Object Oriented Programming. Access to data is tightly controlled via public interface functions “Setters” to set data members
SetHeight(3); “Getters” to obtain values of data members
y = myTime.Write();Here myTime is an object of type Time, say.
Built in operators
int, char, float, double have built in operators + * / % etc classes do not have them
like structs they have . (dot) and = You have to define and implement your own
operators It is possible but perhaps beyond this course to redefine
the built in operators to work on YOUR class.
Specification an Implementation
Like Abstract Data Types (ADT’s) there are two parts to classes
1. the specification – where one declares the class
2. implement the function definitions i.e. to code the functions. This is not crucial but it is how it is discussed in many text books.
Key Points so far Shows practical elements of object oriented programming .
Information hiding Via class syntax
Loose coupling Classes are very self contained There only interaction with other parts of a project is via its public interface
which can be strictly monitored They are very independent to other parts of your program They can therefore be developed and tested in isolation
They result in more robust code They are easy to reuse/ adapt/extend/
They are ideal for team programming For large project they are more productive
They are ideal for large programs
Constructors and Destructors Constructors guarantee initialisation and are called
when an object is created Usually we want to set up an object with certain
values for its data. Special member function called a constructor will
“construct” or initialise any instance of our class exactly how we want to.
The constructor is called at the time of object instantiation! i.e. when we create an object of that class.
Revised class declarationclass Time {public:
void setTime (int hours, int minutes, int seconds);void print24hour();
void print12hour();Time(int initHrs, int intMins, int initSecs) //constructor
Time (); // overloaded constructor~Time(); //destructor
private:int hour;
int minute; int second;};
Define constructor
Time::Time(){
cout << “Setting the time” << endl;hrs=0;
mins=0; secs = 0;}
//Note the constructor has the same name as the class itself//constructors have NO RETURN TYPE not even void.
Purpose of constructor and destructor
They ensure that objects created are properly initialised They ensure that they are properly destroyed
So they can sort out any garbage collection, for example memory de-allocation.
Once this is done properly then class can be used more reliably.
Summary A class can be used to implement an ADT A class specifies members - the data and operations
(methods) Information hiding is a central idea to classes class members are private by default so if you forget to
include an interface they will be private by default Interface functions are explicitly labelled as public Classes provide automatic initialisation through constructors Classes provide a place for garbage collection through
destructors. Creation of an object is achieved via a declaration
statement.
Structures versus Classes On the surface structures and classes are virtually identical,
so why introduce the class? Answer is rooted in C. A C struct is valid in C++. C has no
concept of private or public all members are public in structs In C++ members are private by default as we are trying to
achieve encapsulation So to be compatible with C a new keyword was added. A struct is a class. Look at displaydatestruct.cpp
Arrays of objects
We can create arrays of objects just as we had arrays of structs previously
Look at display.cpp
Pointers to Objects
As you know you can access a structure directly, or through a pointer. In like fashion you can access an object either directly or by using a pointer
Recall that a pointer stores an address To access an element of an object when using the
actual object itself use the dot operator. When using the pointer we use the -> (dereferencing operator)
See example p_Example.cpp
Extending classes
Here we derive one class from another The derived class is called the child class or
the subclass The class derived from is called the
superclass or the parent class If one imagines set theory the parent class is
a sub set of the child class Inheritance is known as a “is a” relationship
Extending in OO programming
Analogy to set theory
Child class
Parent class
enum ZoneType {EST, CST, MST,
PST, EDT, CDT,MDT}class ExtTime:public Time {
public: void Set(int hours, int minutes, int seconds); void print24hour(); void print12hour(); ExtTime(int int Hours, int initMins, int initSecs, ZoneType initZone); ExtTime(); private: ZoneType zone; };
int main() {ExtTime1 A;
return 0;}
instantiate object A of typeExtTime
Further Ideas
C++ and other object oriented languages take things further.
For example, Suppose we had a problem modelling vehicles
We need an Abstract Data Type (ADT) for a vehicle
Vehicle ADT
DATA:PositionSpeedheading
OperationAccelerateBreakTurnDisplayInfo
Suppose we want to model a bus
A bus is a kind of vehicle It would be good if we could make use of our
vehicle implementation and extend it to include bus specific data members and operations
Bus ADT
bus is a kind of vehicle plus
DATA:
nPassengers
Operations:
board passenger
alight passenger
DisplayInfo
This can be done using Inheritance
A class can inherit data and operations from another class
It can also redefine operations (this is called polymorphism) so that they work with the new class e.g DisplayInfo will need to be adapted to display bus
specific information as well as vehicle information. This process of inheritance further extends the
programmers ability to reuse and extend code already written.
These principles, lie at the heart of OOP Information hiding Inheritance and polymorphism
Object Oriented Programming
Identify what the goals are for a problem Identifying the objects that make up a
problem Identifying how the object in a problem
interact with each other in order to satisfy the goals
Specifying ADTs that describe the objects and operations needed for the problem
JAVA
The second module at Oxford in JAVA takes these concepts further
JAVA is a “pure” object oriented language with a distinct C++ flavoured Syntax
It is pure in the sense you cannot program in Java without classes.