Classy Programming
When and why use classes
Copyright © David B. Sher 1999




Classes organize medium size and large programs. Classes bring together a set of connected data and the functions that manipulate the data. The data in a class is always properly initialized and valid. Every part of a class is designed for this purpose. If in your program your class or some part of your class does not achieve these goals, then your program is not properly designed.

An example of a class would be a rational number. The data in the class is the numerator and the denominator (both integers). These will be initialized either with 0/1 by default or with an integer. The data can not be changed except through a member. This prevents inconsistent changes in the data (like dividing by 0). One of the major purposes of a class is to prevent inconsistency by limiting access to the data. One must use members to access the data.

A class defines a type in the language. You can define a variable then which contains an instance of the class, otherwise known as an object. Each variable will share the same functions (with some exceptions) but will have its own data. So each rational variable will have the same addition member but they will have different values in their numerators and denominators. You can also define an array of rationals or a function that returns rationals. You can write functions that accept rationals as parameters but in C++ they must be reference (&) parameters. In Java they are always reference parameters.

A class defines a type in the language.  It defines a kind of thing, not a particular thing.  Usually you only define a class if there are going to be several of them in a program.  For example, a solitaire program might have a class called card.  This class would have as its data, the number of the card (A,K,Q,J,10,9,8,7,6,5,4,3,2) and the suit of the card (§,¨,©,ª).  It would have member functions that display the card to the player and check if the card is equal to another and access the suit or number of the card.  The card class would not be a card but describes a card and describes how a card can be accessed or modified.  The cards in the game might be in an array of type card, the card to be played might be held in a variable of type card.   If you define a class called card you should not define a variable called card since that would be confusing.

You can define a variable then which contains an instance of the class, otherwise known as an object.  Each variable will share the same functions (with some exceptions) but will have its own data.  So each rational variable will have the same addition member function but they will have different values in their numerators and denominators.  Each card variable could have different suits, but every card variable would have a suit.  You can also define an array of rationals or a function that returns rationals.  You can write functions that accept rationals as parameters but in C++ they must be reference (&) parameters.  In Java they are always reference parameters. 

Declaring Classes

Since classes are large combinations of data and functionality they require a comment initially that explains what function they serve in their program or programs. Java and C++ use the class keyword to start a class. So in either language the rational class would start with:
// provides a rational number
class rational
{

In both languages the members follow. These members are either data or functions. The data members contain values and each instance has its own values in its data members (unless you define a data member as static which makes the data global to all instances). You can also define constants that are members (in C++) and types that are members.

Classes control the accessibility of their members. Members that are private or protected can only be accessed by the class' member functions and no other functions can use them. public members can be accessed through the variable name. If print() is a member function of rational and rusty is a variable of type rational then rusty.print() accesses rusty's print member.

In C++ you create access areas of the class by writing protected: or public:. So in the rational class you would see:
// provides a rational number
class rational
{
public:


void print(ostream& out) const // prints a rational to an ostream
{ out << numerator << "/" << denominator; }

protected:

int numerator, denominator; // the numbers whose ratio is the number

}; // end of rational
 

In Java you declare the access for each member of the class. Each member is declared as being public or private. So in Java the equivalent code looks like:
/** This class provides rational numbers and methods to manipulate them
*/
class rational


public void print(java.io.PrintStream out) // prints a rational to a PrintStream 
{ out.print(""+numerator + "/" + denominator); }

private int numerator, denominator; // the ints whose ratio is the number

} // end of rational

Private and protected data members can only be manipulated by the methods of a class so if such a data member achieves an inconsistent value (like a denominator of 0) you need only search the members of the class for the bug.

Members

Data members of a class hold the data of an instance.  They define what an instance has in it.  Each instance has the same data members (every rational has a numerator and denominator, every card has a number and a suit) but the data members can have different values (one card might be 4ª, another card might be Q©). 

Member functions are like other functions except that the member functions of an instance access the data of that instance.  So if a card had a getSuit member function and qh was an instance of card with value Q© then when the getSuit function accessed the number data member it would get Q,  but if qh had the value 4ª, then it would get 4.  Hence each instance has the same member functions but they access the instance’s data, so each instance’s member functions will behave differently.  

You can not access the members of a class in general because a class is not data (what is the value of int and is it smaller than the value of float?).   You can access the public members of any instance of a class.  So if qh is a card and getNumber is a member function of card, you can get the number of qh by calling qh.getNumber(). 

The members of the class can use other members just by naming them so the printCard member of card could call the getNumber member with getNumber().  This is because the printCard member can only be called from an instance (qh.printCard()) so it would know which card it is getting the number from.  If printCard needed to call the member of another instance it would need to mention it (otherCard.getNumber()). 

The data in classes is accessed through accessors, member functions (or methods) that do not change the data in the class but calculate and return or print information about them. print is an example of an accessor. In C++ accessors follow their definition by the word const which indicates that the data of the class will not be changed by the function.

A method that is used to change the value of the data in a class is called a modifier. An example of a modifier for the rational class could be double which would double the numerator of a rational. When you design a modifier you must be careful to avoid making the data inconsistent. If an instance of a class becomes inconsistent or invalid you need only check the modifiers and constructor for the bug.

Initializing

Every class requires a constructor or initializer; this method initializes all the data members in a class. The constructor looks like a public member with the same name as the class. The constructor has no type since it exists to initialize a class. In C++ the constructor is called whenever a variable of the type is declared. As shown below the constructor for the rational class would look like:
// provides a rational number
class rational
{
public:

// initializes the numerator and denominator
rational(int num=0, int den=1) : numerator(num), denominator(den)
{ assert(denominator != 0);}

The default parameters creates a ratio when two arguments are present creates an integral rational number (int/1) when only one argument and will initialize with 0/1 when no arguments are presented for initialization. It also checks to make sure that the denominator can not be 0.

You can then declare rationals in C++ like this:
rational r1, r2(3), r3(7,5);
r1.print(cout); cout << " "; r2.print(cout); cout << " "; r3.print(cout); cout << endl;
This code would output:
0/1 3/1 7/5
 

Java does not have default parameters so to achieve the same effect you need three constructors with no, one and two parameters.
/** This class provides rational numbers and methods to manipulate them
*/
class rational

public rational() {} // use default values
public rational(int num) { numerator = num; } // use default denominator
public rational(int num, int den) // initialize data
{ numerator = num; denominator = den; }
private int numerator = 0, denominator = 1; // rational number initialized to 0/1

}

// tests rational class
class app
{

void main() // executes when stand alone
{

rational r1 = new rational();
rational r2 = new rational(3);
rational r3 = new rational(7/5);
r1.print(System.out); System.out.print(" ");
r2.print(System.out); System.out.print(" ");
r3.print(System.out); System.out.println();

} // end of main

}//end of app

This code would output:
0/1 3/1 7/5

If you approach classes this way you will be able to organize your program with classes.