Notes on Header Files

Notes on Header Files

Machine Problem 4 asks that you place the class definition in a header file.  I seem to forget that I need to explain everything to this class in writing, so what follows is a discussion of header files and their importance.  

When you write a definition of a class such as the one for this machine problem, you expect to use it more than once.  Ideally, you will need it for several programs.  It is not really practical to "cut and paste" the definition for the class into each program where you use it.  C++ allows you to put the class definition, or any other C++ code, into a header file and then ask the compiler to "cut and paste" the contents of the file into a program by using a #include complier directive.  Statements preceded by a # are called preprocessor directives and are executed prior to the compiling of the program that contains them.  They usually involve the definition of symbols or the inclusion of code from other files into the current program.  You have been using these commands to include header files such as iostream.h and apstring.h into your programs throughout the course.  You are allowed to create your own header files and then include them in any program that needs them.   One important difference between the header files that you create and system header files such as iostream.h is that the system file names are included between angle brackets while your own header files, which reside in the directory with your program,  have their names included between quotation marks. 

So when you write your class definition, place it in a file named clubmember.h  Then write a test program that uses the class and place the statement

#include "clubmember.h"

at the beginning of this file.  The class file will be included and all of the class methods may be used in the test program.

In the first example below, the class definition is included in the same file with the test program.  If the class is to be used with another program, you must use the text editor to move it into the new program.  The test program may give you an idea of what to do to test your class.


// class1.cxx
// elementary class demonstration

#include <iostream.h>

class person {

        private:
           int idno;
           int age;
           double salary;

        public:
           person() { idno = 0;
                      age = 0;
                      salary = 0;
                    }
           person( int a, int b, double c)
                {
                   idno = a;
                   age = b;
                   salary = c;
                }
           void print()
           {
              cout << "ID No " << idno << endl;
              cout << "Age:  " << age << endl;
              cout << "Salary: " << salary << endl;
           }


           void readit()
           {
              cout << "Enter ID Number ";
              cin >> idno;
              cout << endl << "Enter Age ";
              cin >> age;
              cout << endl << "Enter Salary ";
              cin >> salary;

           }

           int getidno() { return idno; }
           int getage() { return age; }
           double getsalary() { return salary; }
};

void main( void )
{
   person one;
   person two( 1111, 25, 45000);


   one.print();
   cout << endl << endl;
   two.print();

   int a,b;
   double c;
   cout << "Enter ID Number ";
   cin >> a;
   cout << endl << "Enter Age ";
   cin >> b;
   cout << endl << "Enter Salary ";
   cin >> c;

   person three(a,b,c);
   three.print();

   cout << endl << endl;
   person four;
   four.readit();
   four.print();

   cout << endl << four.getidno();
}

In order to avoid having to copy the class definition into another program, create separate files as follows.


// person.h
// header file for person class
#include <iostream.h>

class person {

        private:
           int idno;
           int age;
           double salary;

        public:
           person() { idno = 0;
                      age = 0;
                      salary = 0;
                    }
           person( int a, int b, double c)
                {
                   idno = a;
                   age = b;
                   salary = c;
                }
           void print()
           {
              cout << "ID No " << idno << endl;
              cout << "Age:  " << age << endl;
              cout << "Salary: " << salary << endl;
           }


           void readit()
           {
              cout << "Enter ID Number ";
              cin >> idno;
              cout << endl << "Enter Age ";
              cin >> age;
              cout << endl << "Enter Salary ";
              cin >> salary;

           }

           int getidno() { return idno; }
           int getage() { return age; }
           double getsalary() { return salary; }
};

// class1.cxx
// Test Program For Person Class
#include <iostream.h>

#include "person.h"

void main( void )
{
   person one;
   person two( 1111, 25, 45000);


   one.print();
   cout << endl << endl;
   two.print();

   int a,b;
   double c;
   cout << "Enter ID Number ";
   cin >> a;
   cout << endl << "Enter Age ";
   cin >> b;
   cout << endl << "Enter Salary ";
   cin >> c;

   person three(a,b,c);
   three.print();

   cout << endl << endl;
   person four;
   four.readit();
   four.print();

   cout << endl << four.getidno();
}

Now, if another program needs to use the person class, it can simply include the file that contains it.  You might be concerned that, since iostream.h is included in person.h and person.h is included in class1.cxx, which also has an include statement for  iostream.h, that class1.cxx contains two copies of the file iostream.h  This will not be the case because iosteram.h, like other standard header files, is protected against multiple inclusions.  The mechanism is illustrated in the bank account class defined on pages 330-331 in the textbook.  You will notice that the class begins with the pair of statements

#ifndef ACCOUNT_H
#define ACCOUNT_H

and ends with the statement

#endif

These again are preprocessor directives.  They are executed before any program that contains this header file is compiled.  They check to see if the symbol ACCOUNT_H is defined and define it if it is not.  The second attempt to include the file that contains this code will find ACCOUNT_H already defined and will skip all the way past the #endif in the process.  In practice, all header files should be protected in this manner.  The name of the chosen symbol to define makes no difference.


You will notice that the header file that contains the bank account class that appears on pages 330-331 in the textbook omits the definitions of the methods.  This is done in order to avoid recompiling all of the code that would otherwise be in the header file each time a program that includes this file is compiled.  For the example above, the number of extra lines to be compiled is insignificant.  However, if the header file contained a large number of lines, the time to compile would be greatly increased.  The "standard" way to implement a class is to create two files: a header file containing the property declarations and function prototypes and an implementation file containing the actual function definitions.  We illustrate this with another class below.


// fruit2.h
// header file for fruit class

class fruit
{
   private:
        int apples;
        int oranges;
        int plums;
   public:
        fruit();

        fruit( int , int, int );

        void setapples( int  );

        void print();

        int getapples();
};

// fruit.cxx

#include <iostream.h>

#include "fruit2.h"

        fruit::fruit() {
            apples = 0;
            oranges = 0;
            plums = 0;
        }

        fruit::fruit( int a, int b, int c)
        {
           apples = a;
           oranges = b;
           plums = c;
        }

        void fruit::setapples( int a )
        {
          if( a < 50 )
            apples = a;
          else
            cout << "Invalid Number of Apples " << endl;
        }

        void fruit::print()
        {
           cout << "--------------------------------" << endl;
           cout << "Apples: " << apples << endl;
           cout << "Oranges " << oranges << endl;
           cout << "Plums " << plums << endl;
           cout << "--------------------------------" << endl;
        }

        int fruit::getapples() { return apples; }

// testfruit.cxx
// test program for fruit class
#include <iostream.h>

#include "fruit2.h"

void main(void)
{
  fruit a,b;
  fruit c(5,12,15);

  a.print();
  b.print();
  c.print();

  a.setapples(27);

  a.print();

  cout << endl << a.getapples();
  cout << endl << c.getapples();
}


Both the implementation file fruit.cxx and the test program testfruit.cxx include the header file fruit2.h  The sequence of commands to compile and run the test program on the DEC Alpha are as follows.

CXX FRUIT
CXX TESTFRUIT
CXXLINK TESTFRUIT,FRUIT

Once the fruit class is developed, the first line will not need to be repeated.  The object file that it produces will remain in the directory to be used anytime the second and third lines are executed.  This will happen often as the test program is being changed and developed.  This avoids taking time to recompile all of the method definitions every time that the test program is changed.  This can be a considerable savings for a large class definition.  Note that, in the implementation file, each method name must be preceded by the class name and the scope resolution operator( :: ).  This is required anytime a method is defined outside the class that contains it.