Computer Science - C++    LESSON 28:  Classes, abstraction, constructors  (PART I)

 

INTRODUCTION:               In Lessons 12 and 15, you were introduced to the concepts of classes, abstraction, and objects.  The next three lessons cover classes and objects in depth.  This lesson will explain the details of class constructors and the implementation of public input/output functions.

The key topics for this lesson are:

A.  Abstraction

B.   Class Constructors

C.  Public I/O Member Functions

VOCABULARY:                  

ABSTRACTION                                 CONSTRUCTOR

INSTANCE                                         MEMBER FUNCTION

ENCAPSULATION                            INSTANTIATION

NON-MEMBER FUNCTION            COPY CONSTRUCTOR

DISCUSSION:                       A.  Abstraction

1.   Abstraction is loosely defined as using software without knowing how it works.  Abstraction is a strategy used to divide up the overall task of developing software.  We wish to develop resources for use in other programs which are portable and convenient to use.

  2.   Classes are used in C++ to promote abstraction.  We will write a class, save it as a separate file, and use it in client programs.  The class accomplishes the abstraction by storing the implementation of an abstract data type in a separate file.  In our work the class implementation will be visible to you, the programmer who uses the class, but it can be compiled to disk to hide the implementation from a client programmer.

3.   An abstract data type (ADT) consists of two conceptual pieces, the data to be stored and the operations we use to manipulate the data.  An object is a programming construct which encapsulates data and operations.  When you declare an object in a program, the object comes with all the operations needed to manipulate that object.

4.   A client program is one which uses the ADT.  The client program will use the abstraction without knowing or relying on the implementation details.  The only requirement is that the client program must use the abstraction correctly.  The programmer will need the specifications of the member functions provided by the object.  This will include a general description of the function, its parameter list, any preconditions about data, and the function postconditions.  At this point in the course you have used three apclasses:  apvector, apmatrix, and apstring.  Given the specifications for each classes' member functions you were able to use these to solve other problems.

5.   The student outline example in Lessons 28-30 is a time abstraction.  Here is an analysis of the time abstract data type, ADT which will be implemented using the C++ class construct.

data to be stored - hours, minutes, and format.

format is either standard (am/pm) or military (24 hours)

operations

constructors - instantiates an object

print time

change the time format - standard versus military

stream I/O functions

boolean comparisons - less than, greater than, equality

increment (++) and decrement (--) operators

See Handout H.A.28.1, time.h.

6.   The entire class is provided as a handout, H.A.28.1, time.h.  The sections will be covered in the student outline in the logical order of development.  As each operation is coded the growing class definition should be tested with a client test program. 

7.   Examining the class definitions at the top of time.h, you will notice that the data fields stored by the class are designated as private.  Any member function defined in the time class can access the private data members, but client programs cannot.  The reason for making the data members private is to minimize dependencies between the client program and the inner implementation of data members in the class.  For example, suppose a string class was implemented with a public array/vector data structure.  The client program could ignore using the public member functions and manipulate the data structure directly.  However, if the implementation of the string was changed to a linked list (a different approach requiring pointers), the client program will also need to be changed.  By making the data members private the client program must use the provided member functions to manipulate the class data members.  If the private implementation scheme changes, the client program should require no changes as long as the member function interfaces (parameter lists) remain stable.

     
Diagram 28-1 

8.   As long as the client program uses the abstraction through the public member functions, changes can be made to the private section without affecting the client program.  However it is important that the interface between the class and the client program remain stable.  This means that the names of the member functions and their parameter lists need to remain constant.

B.   Class Constructors

1.   When a standard variable (int, char, double) is declared in a C++ program, memory is allocated for that variable.  However, the contents of that memory address are uninitialized.

  2.   The purpose of a class constructor is to allocate memory to store an object and to initialize that object with appropriate data.  One of the compelling reasons for using classes is the ability to guard against bad input of data.  When we use this code,

time  t1;

time  t2 (4,9,'p');

      we are both allocating memory to store the objects t1 and t2, but we are also initializing them with appropriate data. 

See Handout H.A.28.1, time.h.

3.   When a class variable is declared two things occur:  an instance of that type is allocated in memory and the private data fields are initialized.  Examining the private data section of the time class in Handout H.A.28.1, time.h, you will notice three data fields.  The three private data fields are: 

        int  myHour;             // stores hours from 0-23

        int  myMinute;              // stores minutes from 0-59

        display  myDisplay;    // an enumeration, standard or military

      The enumeration    

        enum display {standard, military}; 

      is declared in the private section.  Only the member functions of the class will know and use this enumeration.

4.   The time class comes with three constructors, one with no parameters, a second with three initializers, and a third with a time parameter:

      time ();

      time (int hour, int minute, char format);

      time (const time &temp);

      In the second constructor the possible values for the char format parameter are:

            'a' for standard am time

            'p' for standard pm time

            'm' for military

 

5.   The default constructor, time (), will initialize a time object with the default values of 0 hours, 0 minutes, and standard (am/pm) format.  The implementation of this constructor is:

time::time()

        : myHour (0), myMinute(0), myDisplay(standard)

{

        // all private variables initialized using initializer list

}

 

      This function has been written outside of the time class definition.  The scope resolution operator (::) tells the compiler that the member function time () belongs to the time class.  Because the time () constructor was declared inside of the class definition, this member function has access to the private data members of the class.  Notice the use of an initializer list.

 

6.   Class constructors will never have a return type.  It is an error to define a return type for a class constructor.

 

7.   When a time class is instantiated in a program using the default constructor, it will automatically be initialized with the values of 0, 0, standard.  A time of 0 hours will be printed as 12 am.

 

time  t1;

      sets t1 to 12:00 am.

8.   The second class constructor includes three initializer values. 

time::time (int hour, int minute, char format )

{

        setTime (hour, minute, format);

}

 

      This constructor will call another member function setTime to take care of assigning values to the private data members.  Here is an example usage of the constructor with initializers: 

time  t2 (3,19,'p');          // initializes t2 to 3:19 pm

9.   The third constructor is called a copy constructor.  This constructor will instantiate a time object as a copy of another time object.

  time::time (const time &temp)

        : myHour(temp.myHour), myMinute(temp.myMinute),

          myDisplay(temp.myDisplay)

{

        // all private variables initialized using initializer list

}

      Notice the use of the period (.) operator to access the private data members of the temp time object.  Here is an example of using the copy constructor:   

time  t2 (t1);  // creates t2 as a copy of t1

      Some subtle things are happening with this short piece of code.  The more important object in this code is the time object t2.  When the copy constructor is called, temp is an alias for the time object t1.  The three assignments are made to the private data members of the time object t2. 

10.  The time object temp which will be copied is used as a const reference parameter.  The use of a reference parameter, time &temp, avoids making a local copy of a time object.  This will give us faster performance.  Making the parameter const will prevent any changes to the time object we are trying to copy. Using a const reference parameter gives us the ideal combination of speed and performance.  Each of the fields of the temp object is assigned to the analogous field of the new time object.

 

11.  The class supplies three constructors.  The compiler will use the correct version depending on the absence or type of parameters used when the time object is declared.  This is another example of function overloading. 

 

          time  t1;                        // uses the default constructor

          time  t2 (4,22,'p');        // uses the constructor w/ initializers

          time  t3 (t2);                // makes t3 as a copy of t2

 

12.  The call of setTime will be explained in the next section.  The values sent to the constructor with initializers will in turn be sent to a call of setTime.  The member function setTime will assign values to the private data members of a time object.

 

 

C.  Public I/O Member Functions

 

1.   The member function setTime can be used to assign new values to a time object at any point in a program.  A correct call of this function looks like this:

 

        time  t1;                         // t1 is initialized to 12:00 am

 

        t1.setTime (3,40,'p');     //  changes t1 to 3:40 pm

 

      Notice the use of period notation.  The object t1 contains many member functions at its disposal.  To invoke one of the member functions provided by the time class requires the use of the period notation.

 

See Handout H.A.28.1, time.h.

2.   Refer to Handout H.A.28.1 for the implementation of the setTime function. 

 

3.   The setTime function is error-trapped for values outside of the appropriate ranges.  It is important that the class constructors initialize the state of an object to appropriate values.

 

4.   Notice that values passed to either the time constructor with initializers or the setTime function are eventually assigned to the private data members.  The user does not have direct access to the private data members.  Any interaction with the private data members must be done using the public member functions.

 

5.   Having implemented a setTime function, we can easily implement a similar getTime member function that initializes a time object from cin.  This member function will use local variables for keyboard input and then call the setTime function.  A member function of a class can call any other member function of the same class to solve a problem.

 

void time::getTime ()

{

        int hour, minute;

        char format;

       

        cin >> hour >> minute >> format;

        setTime (hour,minute,format);

}

 

      A sample call of function getTime would look like this.

        cout << "Enter a time (hr, min, format): ";

        t1.getTime();

 

      This will prompt the user for the three local values inside t1.getTime.  The function uses setTime to assign the 3 values to the private data members of the time object t1.

 

See Handout H.A.28.1, time.h.

6.   The print () member function allows the user to print the time stored by a time object.  This print() function uses two other functions to take care of formatting the output of hours and minutes.  See Handout  H.A.28.1 for the implementation of printMinute, printHour, and the print member function.

 

7.   One more member function related to output allows the programmer to change the format of a time object.  The changeDisplay member function works as a toggle switch, allowing the programmer to change the format of a time object.  The internal storage of myHour and myMinute will not change;  only the output format will change.

 

void time::changeDisplay()

{

        if (standard == myDisplay)

                myDisplay = military;

        else

                myDisplay = standard;

}

      The operation will be performed on the class object which invokes the changeDisplay member function.  For example, the code

t1.changeDisplay();

      will cause the myDisplay field of object t1 to be switched. 1

8.   The member functions covered in section C are called public I/O functions because they allow the client program (the public) to perform basic I/O operations on time objects.

SUMMARY/REVIEW:         So far we have covered about one-third of the development of a time class.  The remaining sections of overloading useful operators will be covered in Lessons 29 and 30.  The lab exercise in Lesson 28 will consist of solving the constructors and public I/O functions for a date class. See pointer tips.

ASSIGNMENT:                    Lab Exercise, L.A.28.1, Date1