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

  6. 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:
  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 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/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.

  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.

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:

  1. To see the errors when you confuse using the dot operator with -> do the following:

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

  3. **** Add the following to the OrderedList class:

  4. **** Add appropriate statements in testList.cc to test the new member functions.

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