C++ Lab - Introduction to Classes, MakeFiles,
and Operator Overloading
All work for this lab is due at class time on Friday, February 26, 2010.
You are to read the lab and do the exercises (and pay attention!). All
exercises marked with **** are to be turned in (some are answers
to questions and some are modifications of code). All coding MUST
be properly documented (IMPROPER or INADEQUATE DOCUMENTATION will result
in a grade of at most 60%.) Proper documentation includes the following:
Copy the header and implementation files of the example programs
from class to your directory. You may download them from the
following links or copy them as follows:
cp ~cpsc/public_html/Spring2010/CPSC270A/lab3/*.h .
cp ~cpsc/public_html/Spring2010/CPSC270A/lab3/*.cc .
Here are links.
Compiling and Linking
To compile a class and create an object file for the class we
compile the implementation file (which has a preprocessor directive
to include the header file). Compile the date class as follows
(if you changed the name of the file you need to change it here):
g++ -c date.cc
This creates the object file date.o.
To use the test program we also compile it to create an object file:
g++ -c testDate.cc
This creates the object file testDate.o. Now the object
files must be linked to create an executable. The following command
does this creating an executable named rundate.
g++ testDate.o date.o -o rundate
Run the program to make sure it works!
Makefiles
Note that with lots of files it can be tedious to go through all
the steps in the process of compiling and linking. A Unix utility
named make helps make this easier.
To use the make utility you must create a file named Makefile.
Makefile consists of pairs of lines in the following format:
target: dependencies
command for building the target
where target is the name of the file to be created, dependencies is
a list of files that target depends on, and the command for building
the target is the compilation command. For example, if we want
to compile date.cc to create the object file date.o, then the
Makefile entry is:
date.o: date.h date.cc
$(GCC) -c date.cc
Copy Makefile to your directory and examine
it. Note that at the top GCC is defined to mean g++ so g++ is substituted
for $(GCC) in the command.
The Makefile entry to link date.o and testDate.o to create the
executable rundate is as follows:
rundate: date.o testDate.o
$(GCC) testDate.o date.o -o $@
The target (in this case rundate) is substituted for $@.
To execute the commands to create a target you type
make target
at the command line. Try the following command:
make rundate
You may get a message saying rundate is up to date (if you haven't
made any changes since you last created rundate). If so, make
a small change to date.cc, save, then execute the make command
again.
How this works: When you type the make target
command, the entry for that target is examined. For each dependency,
the shell checks its dependency list (and so on up the chain of
dependencies) to see if any changes have occurred since the
corresponding target was last created. If any changes have been made
(or the target has not yet been created), the command is executed.
This continues back down the chain of dependencies until all
dependencies for your target are up to date. Then the command
for your target is executed.
What all this means is that everytime you make a change all you need
to do is type make finalTarget and all the necessary
re-compilations and linking will take place (and the unnecessary ones
won't).
Some Typical Errors in Using Classes
Do the following:
- In date.cc comment out the #include "date.h" preprocessor
directive. Recompile using the make command (make rundate).
What error messages do you get?
- Uncomment the preprocessor directive, then in the .h file
remove the semicolon at the end of the class declaration (after the
final end-brace). Recompile. What error messages do you get?
Put the semicolon back.
- Comment out the implementation of the leapYear method in
date.cc. Recompile. What happened?
Recompile. What happened? Why?
- In testDate.cc add parentheses to the declaration of
firstDate - that is, change it to
Date firstDate();
Compile to see what happens.
- Now change the statement so it looks like Java:
Date firstDate = new Date();
Compile to see what happens.
- Finally modify the statement so that firstDate is a pointer to
a Date object. Compile again. Leave firstDate as a pointer and change
any other statement(s) so that the program compiles and works
as it originally did.
Exercises involving the Makefile
Do the following:
- **** One syntax issue in Makefile -
the second line for each target must contain
a tab. Remove the tabs on the second line of the target rundate,
then type make rundate. What error message do you get? Put the
tabs back in.
- **** Add lines to Makefile so make can be used to compile
and link testDate2.cc. Make sure what you did works!
Operator Overloading
Recall that date2.h and date2.cc contain an enhanced version of the
Date class that includes 3 overloaded operators - the relational
operator ==, the insertion operator <<, and the increment operator ++.
- Compile date2.cc and testDate2.cc and link the resulting object
files to create an executable. Run the program to see how it works.
- **** Now add a function to overload the > (greater than) operator.
Your function should return true if this Date object is
"greater than" (later in the calendar) the Date passed as a parameter.
For example, 4/25/2009 is greater than 3/10/2009. Note
that you must add a prototype for the function in the header file
and implement the function in the .cc file. Be sure to document
your function. Make sure date2.cc compiles.
- **** Add a statement to testDate2.cc test your operator.
Run your program with several dates to make sure it works.
- Overloading the insertion and extraction operators
The modified Date class includes code to "overload" the insertion
operator << so it can be used to output Date objects.
Note that the insertion operator
takes two arguments - an ostream object on the left (such as cout)
and some other data type on the right (the type of data you are
inserting in the output stream).
We can overload the operator so it can be used to output
objects of a type we define BUT it will not be a member of the
class. However it needs access to member data in the class. C++ allows
a function that is not a member of a class to have access to class
members by declaring the function to be a friend. Study the
declaration and definition of the insertion operator in date2.h and
date2.cc.
**** Now add code to the class to overload
the extraction operator >>.
Your code should read the date as three integers (month followed
by day followed by year), separated by white space. That is
the code for the extraction operator should perform the
reading of the three integers (one at a time as usual). It
should just do the reading - any prompts would be in the
program that uses >> (and, of course, any prompt should let the
user know the order to read in the integers - please read
month, then day, then year). The prototype
is as follows:
friend istream& operator >> (istream & stream, Date & d);
As with the insertion operator this is not a member function.
- **** Modify testDate2.cc to use >> to read in the dates.
Provide a helpful prompt for the user!
Test the program.
Working with Linked Lists
The linked list example we examined in class used two classes - a
SimNode class in which each node had two data fields (one for time
and one for type) and a pointer to the next SimNode, an OrderedList
class which was a linked list of SimNodes kept in ascending order
according to the time field, and a test program.
Separately compile the SimNode class (snode.cc), the OrderList class
(olist.cc), and the test program (testList.cc) to get object files.
Now link the files with the following command to get an executable
named runlist as follows:
g++ testList.o snode.o olist.o -o runlist
Run the program, comparing the behavior to the code.
Exercises: Open testList.cc and do the following:
- To see the errors when you confuse using the dot operator with
-> do the following:
- Change list.display() to list->display() then make runlist.
See what error message was generated.
- Correct the earlier error and now change node->display() to
node.display() then make runlist and see what error message is
generated.
- **** In the last loop that removes the nodes in the list one at a
time, change the 5 to a 6. Recompile and run. What happens?
Why did it happen? What would this error have been called in Java?
Write your answers somewhere to turn in (it can be in your email).
- **** Add the following to the OrderedList class:
- A data member length (type int) that keeps track of the
length of the list. Initialize and update the variable where needed.
- A member function that returns the length.
- A member function isEmpty that returns true if the list is empty
and returns false otherwise. (The return type should be bool.)
- **** Add appropriate statements in testList.cc to test the new member
functions.
- **** Add to the OrderedList class
a member function remove
that goes through the list and removes all nodes of a given type.
The type should be passed as an integer parameter.
Be sure to free the memory (delete) for nodes that are deleted HOWEVER
it is often wisest to get the code right first then add in your
delete. Add code to testList to test the remove function (or
write your own test function).
Submit the following
Tar or zip your directory that contains the files for this lab and send
the tar file as an attachment in an email. Please delete all .o files
before tarring. Also submit written answers to the questions in the lab
handout. Hand these in on a piece of paper OR type them into the body
of the email. Do not include them in an electronic
document you have placed in your tarred directory.