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

Classier Classes


Constructor Member Functions

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.

rainbow.gif (2243 bytes)

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);
}

rainbow.gif (2243 bytes)

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.

 

rainbow.gif (2243 bytes)

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);
}

rainbow.gif (2243 bytes)

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.

 

 

rainbow.gif (2243 bytes)

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
}

rainbow.gif (2243 bytes)

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


 

Separate Compilation

WHY?


 

Separate Compilation Example

Let's divide the "coordinate" class into several files.

rainbow.gif (2243 bytes)

// 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;
}

rainbow.gif (2243 bytes)

//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;
      }

rainbow.gif (2243 bytes)

 

// 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();
}

rainbow.gif (2243 bytes)

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

 


 

EXAMPLE: DIVE OBJECT (ANALYSIS)

Problem: To represent individual dives in a dive competition such that the score for each dive can be made available easily.

Some Questions:

  1. What is the object?

  2. What defines the state of that object?

  3. What are the functions defining the behavior of that object?

Some Answers:

  1. Since it is a single dive which is scored, that could be the object.

  2. Degree of difficulty, judge's scores, (other possible who, when, where)

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

 


EXAMPLE: DIVE OBJECT (Specification)

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
};

 



EXAMPLE: DIVE OBJECT (Implementation)

 

// 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;
}

Compiling this example

  1. Create a directory for this project.
  2. Download the relevant files main.cpp, dive.cpp and dive.h either

    1. by clicking here and selecting each file and saving each one

    2. Or by getting on UNIX and copying the files using the command
      cp /home/wild/public_html/cs250/SourceCode/Chapter2/Dive/* .

  3. Compile the Files using the commands
    g++ -c main.cpp
    g++ -c dive.cpp

  4. Link the files together using the command
    g++ -o dive main.o dive.o

  5. Execute the program using the command
    dive


EXAMPLE: DIVE OBJECT (Testing)

 

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


 

EXAMPLE Composite Classes: Student Registration (Analysis)

Problem: To build a student registration system using objects oriented analysis, design and implementation.

Some Questions:

  1. What are the objects?

  2. What defines the state of each object?

  3. What are the functions defining the behavior of each object?

Some Answers:

  1. Course offerings and a Student's Schedule are possible objects (so are Students, Professors, Books,...)

  2. For a Course: name, section, credits
    For Schedule: Student ID (who), semester (when), number and list of courses (what)

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


 

EXAMPLE Composite Classes: Student Registration (Specification)

 

//*********************************************
// 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
};

rainbow.gif (2243 bytes)

 


 

EXAMPLE Composite Classes: Student Registration (Implementation)

 

 

//*************************************************
// 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';
  }
}


 


 

EXAMPLE Composite Classes: Student Registration (Testing)

 

// 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;
}

Compiling this example

  1. Create a directory for this project.
  2. Download the relevant files main.cpp, register.cpp, course.h Rinput.txt

    1. by clicking here and selecting each file and saving each one

    2. Or by getting on UNIX and copying the files using the command
      cp /home/wild/public_html/cs250/SourceCode/Chapter2/Register/* .

  3. Compile the Files using the commands
    g++ -c main.cpp
    g++ -c register.cpp

  4. Link the files together using the command
    g++ -o register main.o register.o

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

  1. What is the object?

  2. What defines the state of that object?

  3. What are the functions defining the behavior of that object?


Copyright chris wild 1998.
For problems or questions regarding this website contact [Chris Wild (e-mail:wild@cs.odu.edu].
Last updated: September 16, 1998.