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!


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:
  1. In date.cc comment out the #include "date.h" preprocessor directive. Recompile using the make command (make rundate). What error messages do you get?

  2. 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.

  3. Comment out the implementation of the leapYear method in date.cc. Recompile. What happened? Recompile. What happened? Why?

  4. In testDate.cc add parentheses to the declaration of firstDate - that is, change it to
        Date firstDate();
    Compile to see what happens.

  5. 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:
  1. **** 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.

  2. **** 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 ++.
  1. Compile date2.cc and testDate2.cc and link the resulting object files to create an executable. Run the program to see how it works.

  2. **** 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.

  3. **** Add a statement to testDate2.cc test your operator. Run your program with several dates to make sure it works.

  4. 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.

  5. **** Modify testDate2.cc to use >> to read in the dates. Provide a helpful prompt for the user! Test the program.

Towers of Hanoi Class

  1. **** 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.

  2. 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

  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.)

  4. **** 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.

  5. **** 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.