C++ Lab - Introduction to Classes, MakeFiles,
and Operator Overloading
All work for this lab is due at class time on Thursday, February 22, 2011.
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/Spring2011/CPSC270A/lab3/*.h .
cp ~cpsc/public_html/Spring2011/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.
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 (don't replace existing lines!)
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/26/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.
Towers of Hanoi Class
- ****
Write a class to represent the Towers of Hanoi puzzle. The basic
class will have an instance variable for the number of disks. The
constructor will take one parameter, the number of disks. Provide
a solve function that will print out the sequence of steps
to solve the problem. The solve function should take no parameters.
It will need to call a recursive helper function to solve the
problem. That function needs 4 parameters: the number of disks
to move, the peg the disks are currently on (just an int representing
the peg number), the peg the disks should end up on (the goal peg),
and the peg that is used as an intermediate.
Implement your class using separate header and implementation files.
Write a program to test it. The input to the program should
be the number of disks.
- Run the program with different numbers of disks. The output should
be a list of steps to solve the problem. For example, if the
number of disks is 3 you would get something like:
Move disk 1 from peg 1 to peg 3
Move disk 2 from peg 1 to peg 2
Move disk 1 from peg 3 to peg 2
Move disk 3 from peg 1 to peg 3
Move disk 1 from peg 2 to peg 1
Move disk 2 from peg 2 to peg 3
Move disk 1 from peg 1 to peg 3
- **** What is the largest value of n you can use to have the
program complete in a reasonable amount of time? (Let me know what
your definition of reasonable amount of time is! Explain what
happened for your n.)
- ****
Add to the class the capability of counting the number of times
each disk is moved (problem 5(b) on page 77). You will need an
array to keep track of this. Make new functions to solve with
the count (copy the two you have - solve and the recursive
helper function - then add the counting code)
and a function to print the count after the puzzle is
solved.
- **** Write a program to test the new functions. Run your program
several times. For "large" n (not so large that the program
doesn't finish executing!) is there a noticeable difference
in running time between the function that does the counts and
the one that doesn't? Explain.
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.