C++ Tutorial

Classes


 
 
 
 
 
 
 

1. Classes
    a. Philosophy of Classes
    b. Syntax of Classes
    c. Data Hiding
    d. The Scope Operator
    e. Instantiations (objects)
    f.  Learning to "read" classes
 
 

Philosophy of Classes, Object Oriented Programming:
  Next (Syntax of Classes)
    Classes are a way of encapsulating data, and looking at the world.  To the programmer, they are ways of encapsulating data, and to the software engineer, or computer scientist, they are ways of looking at the world.  The first thing to realize about classes is that they are not just things that you have to put in your programs, but they are a way of programming.  Instead of thinking about a program as a procedure of the computer does this, this, that, this, and then something else, we can instead think of a program as, the database tells the program that it has a record for John Smith, and the program reacts to that by outputting the name John Smith (for example).  This is of course a simple example, but it is none the less important.  Instead of merely looking at a program as a procedure, one can look at it, instead, as an interaction between objects, and to that end, there is no more free floating programs in Java, which is highly regarded as the second coming of C++.
    So, given that, let us look at a simple example of two ways of doing things.  We have a program segment which must make a robot push a button depending if the room is too warm, or too cold.

(it is assumed that there is a function which will push the correct button given a certain input)

void adjustTemp(int roomTemp, int *addressOfRobotControl) {
  if (roomTemp > MAX_ROOM_TEMP) {
    pushButton(addressOfRobotControl, PUSH_COOL_BUTTON);
  } else if (roomTemp < MAX_ROOM_TEMP) {
    pushButton(addressOfRobotControl, PUSH_HEAT_BUTTON);
  } else {
    // the room is just the right temp, so do nothing.
  }
}

One can immediately see that this is a bit abstracted, though it does not appear very aesthetically pleasing.  On the other hand, consider an example done using classes:

assume member functions:
  class Robot {
     enum ButtonType {
        Cool_Button, Heat_Button
     };
     void pushButton(ButtonType WhichButtonToPush);
  };
  class Room {
    public:
     void adjustTemp(int currentTemperature, Robot & robot);
     static int maximum_temperature;
     static int minimum_temperature;
  };

Now, the implementation is:

  void Room::adjustTemp(int currentTemperature, Robot& robot) {
    if (currentTemperature > maximum_temperature) {
      robot.pushButton(Robot::Cool_Button);
    } else if (currentTemperature < minimum_temperature) {
      robot.pushButton(Robot::Heat_Button);
    } else {
      // If the room is the right temperature, do nothing.
    }
  }

    The real reason that this is better is that there are many of the features of Object-Oriented Programming here, by this we mean programming using the Class world view.  Because of the fact that everything is contained within a class, one does not have to worry about conventions such as using all capital letters, which detracts from readability, and using things such as the '::' operator we ensure that we know where from everything comes.  Though, it may not be your style, even so, it is good to know anyway, because of the encapsulation idea.

    Beyond the philosophy of classes, one must know the syntax.  This will be gone over below.

Syntax of Classes
   Next(Why Data Hiding?)             Prev(Philosophy)     Top (Classes)
    For classes there is an interface of the class, and an implementation of the class.  The interface of the class is always in a file which is called the same name as the class, with a .h appended to it.  So if the class's name is Pixel, then the file in which the interface for that class will be in is Pixel.h.  The interface of a class should not contain any implementations for the member functions for that class.  All it should contain are prototypes and member variables.  Everything else should go in the implementation file.
    A class has multiple parts, one part, which is preceeded by a 'public:' keyword, can be accessed and seen by anything, in or out of the class, variables which are in this area can be modified by things outside of the class as well.  Typically, no variables are defined in the public section.  What typically goes in this section is function which are used to access features of the class, or function which view or change the variables of the class in a controlled way, and update the other variables of the class which, potentially, depend on other variables of the class when those variables are changed.  The other part is preceeded by a 'private:' keyword.  Nothing in this area can be accessed or modified, outside of the class.  This is usually where member variables are declared, and function which can access the class, but should not be used by anyone outside the class (these are such things as auxiliary functions, and default methods which one does not want to allow people to use, such as the copy constructor and the assignment operator).
    These parts are called the body of the class, and are only part of the class.  The entire class consists of the keyword class, followed by the name of the class, in the name of the class, one must specify whether this class inherits from any other class(es), the name is followed by an open curly brace ( '{' ), the body of the class, and a closed curly brace, ( '{' ), followed by a semicolon.  More succinctly,

  class classname : classToInheritFrom {
        <body>
    };
    ClassToInheritFrom may consist of a list of classes, each of which may optionally be declared either public or private, meaning that the things which are inherited from the class can be seen from outside the world or not.  The simplest class, one might think is:

  class Foo {
  };

    As, in this case, it has an empty body, and does not inherit from anything, though similarly, this class cannot do anything either.  A more complex class is:

  class Foo {
    public:
     void set_m(int nextM);
     int get_m();
    private:
     int m;
  };

This may look a bit unnerving but let us take it apart.  This is a class, we know this because of the 'class' keyword.  Further, it has two member functions which are visible to the world, set_m() and get_m().  These are called accessor functions, because they each access the member variable m, which is not visible to the world (it is in a private section of the class, because of the 'private' keyword), one is used to return the value of m and one to set the value of m to its argument.  The implementation is straight forward for these function due to the method in which the functions were named, with their purpose explicitly stated.
    The above is called the interface of the class, and should be placed in a file called Foo.h.
    The implementation to this class is listed below, and should be placed in a file called Foo.C.  One small note is that we must use #include "Foo.h" to let the compiler know what a Foo is, because it assumes that each file is separate, and has no relation to any other file.

  #include "Foo.h"
  void Foo::set_m(int nextM) {
    m = nextM;
  }
  int Foo::get_m() {
    return m;
  }

Why Data Hiding?
   Next(The Scope Operator)      Prev(Syntax)      Top(Classes)
So, why do we even have these functions, couldn't we just as simply replace these functions with what they do?  Yes, but consider the following:

class Baseball {
   public:
     void pitch();
     void hit();
   private:
     int x, y;
};

#include "Baseball.h"
void Baseball::pitch() {
   int num = random() % 100;  // Get the random percentage of doing something on this pitch.
    // We say that the catcher is positioned at 0,0, the pitcher is positioned at 10,10

   if (num < 30) {  // We have a 30% chance of missing everything entirely...
    // If we miss everything then simply throw it back to the pitcher...ignore wild throw benefit.
     x = 10;
     y = 10;
  } else if (num < 50) { // We have another 20% chance of hitting the batter...
    // if we hit the batter, the pitcher gets the ball back also...
    x = 10;
    y = 10;
  } else if (num < 80) { // We actually have a 30% chance of getting a strike...
    // If it is a strike then leave the ball at the catcher, as someone might try and steal...
    x = 0;
    y = 0;
  } else {
     hit();
  }
}

void Baseball::hit() {
   int num = random() %100; // Get the random percentage of what we did with the ball.

   if (num < 10) { // We have a ten percent chance of fouling off the ball...
     x = 10;
     y = 10;
   } else if (num < 30) { // We have another 20% chance of hitting the ball into the infield
     x = 13;
     y = 12;
   } else if (num < 50) { // Another 20% chance of hitting a Pop Fly
     x = 10;
     y = 10;
  } else if (num < 80) { // 30% chance of hitting a grounder out past the infield...
     x = 23;
     y = 20;
  } else if (num < 90) { // 10% chance of hitting a home run...
     x = 10;
     y = 10;
  } else { // We missed...leave it at the catcher.
     x = 0;
     y = 0;
  }
}

This is a simple example, and one can see quickly that simply setting something outside the object, possibly by an agent which does not know the internal workings of the object, could be dangerous, and have problems with future runs of the functions.
 

Syntax of Classes and the Scope Operator (::)
    Next(Instantiation)       Prev(Why Data Hiding?)    Top(Classes)

    The scope operator deserves explanation, as I used it multiple times above, yet have not explained it.  Consider the following codelet:
   int Foo::get_m() { return m; }
The scope operator is in between Foo and get_m, but what does it do?  In short it changes your scope to be 'inside of' Foo.  Normally, a function cannot see m, though when you use the scope operator, you can suddenly access it.  Unfortunately, and fortunately, you still cannot modify or read internal variables with the scope operator if you are not in a member function (previously defined in the class interface) of the class.  Though this allows you to tell the compiler that a function is really a member of a class, and not just a function out there, because recall that a one can have both:

void get_m();

and

class Foo() {
   get_m();
};

and that both of these are different functions.  This is actually due to the overloading behavior of C++, allowing a function to be specified multiple times, so long as it has a different function signature each time.  In short what this means, is that we can have the above specification because in reality, the two functions are:

void get_m();

and:

void Foo::get_m();
 
 
 

Instantiations
    Next(Learning to Read Classes)        Prev(Scope Operator)     Top(Classes)

    I have previously gone over the purely abstract qualities of Classes, and more to the point, have not really gone over how to use them.  In order to use a class, you must create an object, an actual instantiation of that class.  This is actually quite easy, and all it requires is to use the class name as the variable type in a normal variable declaration, i.e. if your class's name is Foo, then:
  Foo fooObject;
Would create a variable, called fooObject, which is an object of the type class Foo.  This object can be used to call any of the member functions of the Foo object, and further, has member variables within it (defined in the class definition) which may be modified, or read.  Consider the following definition:

class Foo {
  public:
    Foo();  // Default constructor, place foo in the position (1,1) and give him 30 health.
    // Put a Foo in a specific location, give him 30 health.
    Foo(int x, int y);
    // Put a Foo in a specific location and give him h health.
    Foo(int x, int y, int h);

    // These are the directions in which a Foo can move.
    enum direction {
       NORTH, EAST, SOUTH, WEST
    };
    // Move the Foo in a specified direction.
    void move(direction whereToMove);

  private:
    int health; // How healthy is this Foo?
    int x, y; // Where is this Foo?  (X,Y coordinates)
};

So, in order to call move(Foo::NORTH), we would have to have an object of type Foo, as the scope operator will not help us.  This is because move is a function of type void, and therefore (because it is not of type static void) must have an instance which it can act on.  So, while:

int main() {
   Foo::move(Foo::NORTH);
   return 0;
}

Would not work (we actually see this because we have not specified what must move, only that whatever must move, it must be a Foo...), though the following would:

int main() {
  Foo myFirstFoo;
  myFirstFoo.move(Foo::NORTH);
  return 0;
}

Because it is telling the Foo myFirstFoo to move to whatever a Foo calls NORTH.  This is essentially how you must learn to read the lines.  Let me move more step-by-step to give you an understanding of what I am talking about.
 

Learning to "read" classes
    Next()       Prev(Instantiation)    Top(Classes)

    Reading classes is just like reading code, only a specific case of doing such.  Reading any line of Code is actually not too difficult.  One must simply learn how to parse the symbols correctly.  Consider the following example from above.
    void Foo::get_m() {
       return m;
    }
This would be read as follows:  There is a function (see the '(' and ')' symbols just after get_m), which returns void (the first thing on the line), it is called get_m (the actual name of the function, after the return type) that 'belongs to' or is 'inside of' Foo (The scope operator (::) should be read as inside of, though sometimes it can also be read as belongs to).  This function is defined as ( read the '{' ) returning m (the return m line) (and read the '}' just as you would a period).  This is naturally an implementation of the member function of Foo, called get_m.  Now, moving onward, consider the following (as Foo was defined in the previous section):
  int main() {
    Foo m;
    m.move(Foo::NORTH);
    return 0;
  }
This should be read as follows, there is a function called main, that returns an int.  It is defined as:  a variable of type Foo is created, using a default initialization (this is parsed this way because Foo is the type of the variable, and m follows it, though there are no parentheses, so we assume that it is being initialized using the default constructor), then m is told to move to whatever a Foo calls NORTH (move is a member function of m, we use the phrase m is told to move because by calling a member function we are sending m a message, namely, that it must move, further, where we tell it to move is to the NORTH, but we aren't sure what a Foo considers NORTH, so we tell it to move in whatever direction is NORTH to it (Foo::NORTH, specified the NORTH inside of Foo).), and that finally, we wish to return from this function (which because it is main will exit the program).