CS 250 Computer Programming and Problem Solving - FALL 1998 |
---|
[ Home | Syllabus | Notes | Glossary | CS250 WEB Version Home Page ]
Need To Know: My philosophy is to teach first what you need to get
started with objects
and later to add refinements which improve the program.
Based on that philosophy I am skipping the following material in Chapter 2 for now:
Program Assignment: Show them the web site link on home page
Constructor functions allow you to set the initial values of
an object when it is created.
This is good idea, so that the initial state of the object is a reasonable one.
For example, in our coordinate class example, we never want the values for x and y to be out of the range from 0 to 1000 and the value for display should only be a 0 or a 1. Without a constructor function, when we create a new instance of this class (by defining some variable like 'point1'), we don't know what values will be initially assigned to the data members. Some compilers will set them to '0', but that is NOT guaranteed.
So Let's write a constructor function for this class. By
convention, the name of the constructor function is the same as the name of the class.
This function is called automatically whenever you define a new variable of that
class.
class Coordinate { public: Coordinate(); void setX(int xValue); void setY(int yValue); void setDisplay(int dValue); int getX(); int getY(); int getDisplay(); private: int x; int y; int display; } // sometime later you define this function
Coordinate::Coordinate() { setX(0); setY(0); setDisplay(0); }
Why must we write "Coordinate" twice when defining it outside
the class?
Well, because it is outside the class, we need to tell the compiler which class it is
for, use the scope resolution operator and then put the member function name - which is
this case is exactly the name of the class - hence it appears 2 times.
Now suppose you want the initial values of the Coordinate to be something other than 0? Well then you can write a different constructor for that case. The constructor we wrote above is called the default constructor, because it is the one used when you define an object in the usual fashion.
Coordinate point1;
This definition automatically "calls" the default constructor and guarantees that the initial values of all data members are '0'; If you want to set these values to something else, you can pass in the values as arguments to a constructor. This will be a non-default constructor. Let's try it.
class Coordinate { public: Coordinate(); // default constructor Coordinate(int xValue, int yValue, int displayValue); void setX(int xValue); void setY(int yValue); void setDisplay(int dValue); int getX(); int getY(); int getDisplay(); private: int x; int y; int display; } // sometime later you define this function
coordinate::coordinate(int xValue, int yValue, int displayValue) { setX(xValue); setY(yValue); setDisplay(displayValue); }
To use this non-default constructor, you add the arguments when defining the object
Coordinate point3(35,78,0);
'point3' will have its initial values set to x=35, y=78 and display =0, Notice that the arguments appear in a funny place for a function call, but it is a special kind of function.
It is even possible to have more than one non-default constructor. Say that you only want to set the x and y value, but will take the default for the display.
class Coordinate { public: Coordinate(); // default constructor Coordinate(int xValue, int yValue, int displayValue); // non-default constructor Coordinate(int xValue, int yValue); // non-default constructor setting only x and y void setX(int xValue); void setY(int yValue); void setDisplay(int dValue); int getX(); int getY(); int getDisplay(); private: int x; int y; int display; } // sometime later you define this function
Coordinate::Coordinate(int xValue, int yValue) { setX(xValue); setY(yValue); setDisplay(0); // the default value }
So how does the compiler know which non-default constructor to use? by the number and type of the arguments. So for example
Coordinate point1; // default constructor called
Coordinate point3(37,78,1); // set all three values
Coordinate point4(567,20); // set only x and y, take default for display
WHY?
Break Big Program into Manageable Pieces
Separate Specification (WHAT) from Implementation (HOW)
Hide Proprietary Software (only export libraries of compiled object code)
Speed the Compilation Process
Let's divide the "coordinate" class into several files.
coordinate.h: header file which contains specification of the class (only the function prototypes - not the implementation).
coordinate.cpp: implementation of the member functions for the coordinate class
main.cpp: test program using the coordinate class
// coordinate.h: header file for coordinate class // just gives enough information so that some one else // can use this class in their program // in other words, it specifies what the class is without // giving away unnecessary details // function prototypes only please
class Coordinate { public: void setX(int xValue); void setY(int yValue); void setDisplay(int dValue); int getX(); int getY(); int getDisplay(); private: int x; int y; int display; }
//coordinate.cpp: files containing definitions (implementation) of // the member functions of the coordinate class // remember to use the scope resolution operator ("::") to help the compiler out
#include "coordinate.h" // need to tell the compiler the specification
void Coordinate::setX(int xValue) { if(xValue < 0) xValue = 0; // clip values if(xValue > 1000) xValue = 1000; x = xValue; } void Coordinate::setY(int yValue) { if(yValue < 0) yValue = 0; // clip values if(yValue > 1000) yValue = 1000; x = yValue; } void Coordinate::setDisplay(int dValue) { if(dValue < 0) dValue = 0; // clip values if(dValue > 1) dValue = 1; display = dValue; } int Coordinate::getX() { return x; } int Coordinate::getY() { return y; } int Coordinate::getDisplay() { return display; }
// main.cpp: main program which uses the coordinate class // here we just write a few statements for testing // so we will call this a test driver
#include "coordinate.h" // Need to tell the compiler about the specification #include <iostream.h> // This is the standard IO class header file // the < > syntax tells the compiler to look in the systems library place // while the " " used for coordinate.h says look in the current directory
void main() { Coordinate p1;
p1.setX(37); cout << p1.getX(); }
Now we have three files - how do we get a working program from them?
On UNIX, you can do the following:
g++ -c coordinate.cpp g++ -c main.cpp g++ -o testCoordinate main.o coordinate.o testCoordinate
Problem: To represent individual dives in a dive competition such that the score for each dive can be made available easily.
Some Questions:
What is the object?
What defines the state of that object?
What are the functions defining the behavior of that object?
Some Answers:
Since it is a single dive which is scored, that could be the object.
Degree of difficulty, judge's scores, (other possible who, when, where)
Keeping with our design principles of hiding data, we would need set/get functions. Also we would like the object to tell us the total points received for that dive.
The header file for a class can serve as its specification.
// DIVE.H - Dive class definition
#include <iostream.h>
class Dive { public: // This non-default constructor sets the score and difficulty when creating the object // INPUT: avg - is the average judge's score // PRE-CONDITION: between 0 and 10 // diff - is the degree of difficulty // PRE-CONDITION: between 1.0 and 4.0 Dive( float avg, float diff );
// Calculates and returns total points for this dive float CalcTotalPoints( );
// Displays the state of this dive (including total points) to the standard output void Display( );
// Sets the difficulty of this dive // INPUT: diff - is the degree of difficulty // PRE-CONDITION: between 1.0 and 4.0 void SetDifficulty( float diff );
// sets the average judge's score // INPUT: score: is the average judge's score // PRE-CONDITION: between 1.0 and 4.0 void SetJudgeScore( float score );
private: float judgeScore; // average judges score float difficulty; // degree of difficulty };
// DIVE.CPP - Dive class implementation.
#include "dive.h"
// Constructor.
Dive::Dive( float avg, float diff ) { SetJudgeScore( avg ); // Notice the use of the member functions by member functions SetDifficulty( diff ); }
// Display the Dive on the console.
void Dive::Display() const { cout << "[Dive]: " << judgeScore << ',' << difficulty << ',' << CalcTotalPoints() << endl; }
void Dive::SetDifficulty( float diff ) { if((diff >= 1.0) && (diff <= 5.0)) difficulty = diff; else // if precondition not met, print error cerr << "Range error in Dive::SetDifficulty()" << " value: " << diff << endl; }
void Dive::SetJudgeScore( float score ) { judgeScore = score; }
float Dive::CalcTotalPoints() const { return judgeScore * difficulty; }
Download the relevant files main.cpp, dive.cpp and dive.h either
by clicking here and selecting each file and saving each one
Or by getting on UNIX and copying the files using the
command
cp /home/wild/public_html/cs250/SourceCode/Chapter2/Dive/* .
Compile the Files using the commands
g++ -c main.cpp
g++ -c dive.cpp
Link the files together using the command
g++ -o dive main.o dive.o
Execute the program using the command
dive
// MAIN.CPP - Main test program for Diving application.
#include "dive.h"
int main() { Dive D1( 8.5, 3.0 ); // create 3 dives Dive D2( 9.0, 2.5 ); Dive D3( 8.0, 3.3);
D1.Display(); // display 3 dives D2.Display(); D3.Display();
D2.SetDifficulty( 3.0 ); // modify difficulty
// Display the Dives again. cout << "\nChanging Dive 2\n"; D1.Display(); D2.Display(); D3.Display();
return 0; }
HAS-A (Class Relations)
So far we have only seen data members which are "normal" C++ data types.
However, there is no reason why the data members can't be OBJECTS themselves.
This relationship between the objects is called a "HAS-A" relationship because one of the objects has a property which is an object itself.
This is also called Composite Classes.
Problem: To build a student registration system using objects oriented analysis, design and implementation.
Some Questions:
What are the objects?
What defines the state of each object?
What are the functions defining the behavior of each object?
Some Answers:
Course offerings and a Student's Schedule are possible objects (so are Students, Professors, Books,...)
For a Course: name, section, credits
For Schedule: Student ID (who), semester (when), number and list of courses (what)
Keeping with our design principles of hiding data, we would
need set/get functions.
IN this case we will set the input from the standard input and get will print out the
data to the standard output
//********************************************* // COURSE.H - Course and Registration classes //*********************************************
// Chapter 2 sample application.
#include <iostream.h> #include <fstream.h>
const unsigned CnameSize = 10;
class Course { public: Course(); void Input( ifstream & infile ); // call by reference so can changed input and output stream void Output( ofstream & ofile ) const;
private: char name[CnameSize]; // course name char section; // section (letter) unsigned credits; // number of credits };
const unsigned MaxCourses = 10;
class Registration { public: Registration(); void Input( ifstream & infile ); void Output( ofstream & ofile ) const;
private: long studentId; // student ID number unsigned semester; // semester year, number unsigned count; // number of courses // Truly Private data member (no access or modifier Get/Set) Course courses[MaxCourses]; // array of courses };
//************************************************* // REGIST.CPP - Course and Registration class // implementations, and main program. //*************************************************
#include "course.h"
Course::Course() { name[0] = '\0'; // can you explain this? }
void Course::Input( ifstream & infile ) // reads from file { infile >> name >> section >> credits; }
void Course::Output( ofstream & ofile ) const { ofile << " Course: " << name << '\n' << " Section: " << section << '\n' << " Credits: " << credits << endl; }
Registration::Registration() { count = 0; // Must be set here since no other way (truly private) }
void Registration::Input( ifstream & infile ) { infile >> studentId >> semester >> count; // truly private "count" is read/written privately // under control of the class memeber functions
for(int i = 0; i < count; i++) courses[i].Input( infile ); // Let the Course object's Input do the work here. }
void Registration::Output( ofstream & ofile ) const { ofile << "Student ID: " << studentId << '\n' << "Semester: " << semester << '\n';
for(int i = 0; i < count; i++) { courses[i].Output( ofile ); // interesting and "natural" division of labor // between this object and the Course objects ofile << '\n'; } }
// MAIN.CPP - Chapter 2 example
// Main program for the Registration application
#include "course.h" // why don't need <iostream.h>?
int main() { // Read a Registration object from an input // file and write it to an output file.
ifstream infile( "rinput.txt" ); if( !infile ) return -1; // check to see if "infile" was opened correctly // -1 indicates error upon return from main.
Registration R; R.Input( infile );
ofstream ofile( "routput.txt", ios::app ); if( !ofile ) return -1;
R.Output( ofile );
return 0; }
Download the relevant files main.cpp, register.cpp, course.h Rinput.txt
by clicking here and selecting each file and saving each one
Or by getting on UNIX and copying the files using the
command
cp /home/wild/public_html/cs250/SourceCode/Chapter2/Register/* .
Compile the Files using the commands
g++ -c main.cpp
g++ -c register.cpp
Link the files together using the command
g++ -o register main.o register.o
Execute the program using the command
register
Programming Assignment: Card Object
Problem: To represent a single individual playing card (exercise 1 in section 2.8.1 page 54)
Some Questions:
What is the object?
What defines the state of that object?
What are the functions defining the behavior of that object?