CS 250 Computer Programming and Problem Solving - FALL 1998 

[ Home | Syllabus | Notes | Glossary | CS250 WEB Version Home Page ]


Sections of chapter 4 to be deferred:

 

Commenting Conventions

While there are many possible commenting conventions, for this course I expect the following:

  1. Each file (which is usually a single object or function) should have a header comment which:

    1. Explain the purpose of the object or functions in that file

    2. Gives the Programmers Name

  2. Each function in the file (including member functions of objects) shoudl have a function header comment which:

    1. Explains the purpose of that function

    2. Gives the PRE-CONDITIONS assumed on the input data and any error handling that is taken if the preconditions are not met.

    3. Gives the POST-CONDITIONS on the output data (what is true about the output). Basically this explains the purpose of the output data in this function.

  3. Any additional comments which you feel another programmer would find useful if they had to make changes to the code.
    For example, a reference to a book where you found the algorithm you used in the function.
    Any constraints on the solution which had to be satisfied by this programmer (the size of arrays, strings, etc which are dictated to you by the requirements vs those you choose as reasonable by yourself (and which are therefore easily changed)).

  4. I prefer single line comments "//" to multi-line comments "/*" and "*/"

  5. However multi-line comments are sometimes useful when debugging to commenting out parts of the code (although I prefer compiler directives for this purpose more).


Composite Classes


Now Let's build a minimal Registration Object (Chapter 2 pp48-52, Chapter 3, pp.90-95)

Let's demonstrate an incremental build strategy.


Incremental Build: building a complex solution a little bit at a time.

Let's use the Student Registration System as an Example of the Incremental Build Process.

Chapter 2: two objects

Even though this is a simple example, it would be worthwhile to simplify it further and build small pieces and test them a little bit at a time.
Here is a possible strategy for doing incremental build
Phase I

  1. Start with Course Object (it is the inner or contained object in a HAS-A relationship with the Registration Object).
  2. Start with only one data member (say credits) and write the Input and Output member functions to only handle that one data member.
  3. Test this simplified classs with a main.cpp containing a test driver.

rainbow.gif (2243 bytes)

 

//*********************************************
// COURSE.H - Course and Registration classes
//*********************************************
#include <iostream.h>
#include <fstream.h>
class Course {
public:
  Course();
  void Input( ifstream & infile );
  void Output( ofstream & ofile ) const;
private:
  unsigned  credits;    // number of credits
};

rainbow.gif (2243 bytes)

  

//*************************************************
// REGIST.CPP - Course and Registration class
//              implementations, and main program.
//*************************************************
#include "course.h"
Course::Course()
{
}
void Course::Input( ifstream & infile ) // reads from file
{
  infile >> credits;
}
void Course::Output( ofstream & ofile ) const
{
  ofile << "  Credits: " << credits << endl;
}

 

rainbow.gif (2243 bytes)

 

// 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;
  Course C;
  C.Input( infile );
  ofstream ofile( "routput.txt", ios::app );
  if( !ofile ) return -1;
  C.Output( ofile );
  return 0;
}
rainbow.gif (2243 bytes)

The complete example is here


 

 

Let's add a few more features to this problem, updating the object specfications as needed

 

Incremental Build: building a complex solution a little bit at a time.


Phase II

  1. Build a simple Registation Object which uses the simple Course Object from PhaseI
  2. Start with only one data member "course" which is not an array initially and write the Input and Output member functions to only handle that one data member.
  3. Test this simplified classs with a main.cpp containing a test driver.

rainbow.gif (2243 bytes)

 

//*********************************************
// COURSE.H - Course and Registration classes
//*********************************************
// Chapter 2 sample application.
#include <iostream.h>
#include <fstream.h>
class Course {
public:
  Course();
  void Input( ifstream & infile );
  void Output( ofstream & ofile ) const;
private:
  unsigned  credits;    // number of credits
};
class Registration {
public:
  Registration();
  void Input( ifstream & infile );
  void Output( ofstream & ofile ) const;
private:
  Course courses; // course
};

rainbow.gif (2243 bytes)

 

rainbow.gif (2243 bytes)

  

//*************************************************
// REGIST.CPP - Course and Registration class
//              implementations, and main program.
//*************************************************
#include "course.h"
Course::Course()
{
}
void Course::Input( ifstream & infile ) // reads from file
{
  infile >> credits;
}
void Course::Output( ofstream & ofile ) const
{
  ofile << "  Credits: " << credits << endl;
}

 

Registration::Registration()
{
}
void Registration::Input( ifstream & infile )
{
    courses.Input( infile );
}
void Registration::Output( ofstream & ofile ) const
{
    courses.Output( ofile );
}


 

rainbow.gif (2243 bytes)

 

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;
  Registration R;
  R.Input( infile );
  ofstream ofile( "routput.txt", ios::app );
  if( !ofile ) return -1;
  R.Output( ofile );
  return 0;
}
rainbow.gif (2243 bytes)

The complete example is here

 


Here is the complete example from Chapter 2


 

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

 

Constructor-initializer

Description Problem: when forming composite classes, how to call the non-default constructors of objects contained within another object?

RULE: all contained objects are created before the containing object
IMPLIES non-default constructor of a contained object must be called before the constructor of the containing object - HOW?

Constructor-Initializer allows arguments to be passed to non-default constructors of the contained classes

Syntax outerObjectConstructor( parameterList) : innerObject1ID(constructorArguments)
Examples
(more examples)
class Line {
public:
  
Line(int x1, int y1, int x2, int y2);  // set x,y coordinates of two end points
private:
   Coordinate point1; // first end point
   Coordinate point2; // second end point
}

Line::Line(int x1, int y1, int x2, int y2) : point1(x1,y1), point2(x2,y2)
{
}

Tips
  • Constructor Initializers can be used for built in data types as well, e.g. the definition of the two parameter non-default constructor for Coordinate could be re-written as
    Coordinate::Coordinate(int xValue, int yValue) : x(xValue), y(yValue) { }
  • warning_big.gif (288 bytes)Putting the nondefault constructor inside the outer constructor would only create a local variable for the outer constructor, e.g.
    Line::Line(int x1, int y1, int x2, int y2) {
       Coordinate point1(x1,y1);
    }
    this "point1" is a local variable in this function and will not set the values of "point1" data member (which is already "constructed" by the default constructor for Coordinates by the time you get inside the constructor for Line)

 


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