C++ - Objects and Classes

Introduction

An object is a computer representation of a physical object. Consider the standard English definition of the word object - all nouns can be treated as objects. By this definition, a person is an object, a table is an object, a chair is an object, a room is an object, etc.

All objects have attributes. A person has arms, legs, a head and a body. A table has a tabletop and legs. A chair could be made of leather. Every object also has uses. A person can walk or run or talk. A table can be used to hold other objects. A chair is used to sit on.

Object-oriented programming is considered superior to standard approaches because we model the real world as closely as possible.

Classes

Every object must have a specific type just like any other data definition. Type definitions for objects are made using the class construct. Classes are not objects - just the definition thereof. Objects are created using the standard variable creation procedures - they are then called instances of the class.

Classes are an extension of the standard struct feature. Struct allows us to group data items together. Classes allow us to group together data items and function and that operate on the data. This makes the object into a self-contained unit, cutting down on reliance on other functions. This is the first important feature of any object-oriented paradigm.

Example:
class Number {
public:
   int i;
   void Output ();
};

In the above example, Number is the name of the new class being created. This class contains a number in the form of an integer. To output the number, the Output procedure is called using the standard "." notation. Since the function is defined as part of the class, it has access to all the data items defined in the class, hence it can read or write to i. The function definition itself can appear outside the class, with the function name preceded by the class name and "::" to indicate that it is a member of that class and not a global function.

Example:
void Number::Output ()
{
   printf ("%u", i);
}
Number aNumber;
aNumber.i = 12;
aNumber.Output ();

Objects are normal data types in every sense. Thus they can be used in functions for parameters and return values. They can be created/disposed using pointers. Just like structs, scope is an important consideration. All the names used within an class are local to that class- only the class name itself is global.

Constructors/Destructors

Every object can have two specially-defined functions called a constructor and a destructor. A constructor is a special function called whenever an instance of the class is created and a destructor is called whenever an instance is destroyed. Instances are created either at the the point of definition or when memory is allocated to a pointer to the class. Instances are destroyed either by pointer deallocation or when a variable goes out of scope.

Constructors are denoted by a function which has no return value and has the same name as the class. Destructors are similar to constructors except that the name is prefixed with a tilde "~".

Example:
class Number
{
public:
   int i;
   Number ( int a ) { i=a; };
   ~Number ();
   void Output ();
};

Number::~Number ()
{
   printf ("End of Number usage\n");
}

Number aNumber (2);

In the above example Number has a constructor with one parameter, an integer. Constructors can have any number of parameters and there may be many different constructors with different parameter sets. Destructors cannot take parameters and they have the Highlander property i.e. there can be only one :-)

When aNumber is instantiated, parameters must be given to satisfy one of the constructors. Then this constructor is called. In this case, the value 2 is seeded into the object through the constructor. When the function in which the declaration occurs terminates or the program ends, the destructor is called.

The constructor can be defined like any normal function, but in the above code the statements form part of the class definition. This is a method of implicitly creating inline functions in classes.

Inheritance

Classes sometimes have similar properties to other classes. Instead of incorporating all the properties of a class into every similar class, the common properties can be extracted and used as a basis for building future classes.

Since all students are human beings and all lecturers are human beings, it would be quite a task to have to redefine human beings for each class of person. Instead we create a base class called a HumanBeing and then derive the Student and Lecturer from it. Student and Lecturer can then inherit the data and function members from HumanBeing. Thus they will be richer objects than their parent class.

In order to inherit from a class, the base class name must be appended with a colon to the class name. Then all the members of the base class become part of the derived class.

Example: 
class ComplexNumber : public Number 
{ 
   int r; 
   ComplexNumber ( int re, int im ) : Number ( im ) { r = re; }; 
   ~ComplexNumber (); 
   void Output () { printf ("%u + %u i", r, i); }; 
};

public denotes that the class inherits all its protection properties exactly as they are in the base class. The constructor in this case calls the constructor for the base class - this must be done. The syntax is to follow the definition by a colon and a comma-separated list of the base class constructors. If a base class has a constructor, then the derived class must have one too.

Output has been redefined to output a complex number instead of a real number. Thus the function has the same name and purpose in both objects although being internally different. This redefinition of functions hides the implementation-specific details and is a key feature of all object-oriented languages.

public/protected/private

Data and functions in a class can be protected from external usage. The labels public, protected and private within a class definition indicate that all the following declarations inherit a particular protection level.

public indicates that the data and function members can be accessed from anywhere.

private indicates that the members can only be accessed from within the class. By default all class members are private, which is why the "public:" is necessary at the top of the class definition.

protected indicates that the members can be accesses from within the class and from within derived classes.

new/delete

new is used to allocate memory for an object i.e. it instantiates a class. delete deallocates the memory. These operators should be used instead of malloc/free because they also cause the constructor and destructor to be called.

new must be followed by the name of the class and parameters for the constructor.
delete has no parameters.

Example:
ComplexNumber *c = new ComplexNumber ( 1, 2 );
delete c;

Polymorphism

Polymorphism refers to the implicit ability of a function to have different meanings in different contexts. Consider the class hierarchy that contains Number and ComplexNumber. If aNumber is defined as a pointer to Number then aNumber can be instantiated as either a Number or a ComplexNumber.

Example:
Number *aNumber;
aNumber = new Number (1);
aNumber->Output ();
delete aNumber;
aNumber = new ComplexNumber (1,2);
aNumber->Output ();
delete aNumber;

In the first case, the Output function in Number would be called and in the second case, it would be called again. This happens because aNumber is a pointer to a Number and not a ComplexNumber. To solve this problem, both Output functions must be declared as virtual. Then the compiler keeps track of which the actual functions associated with each object and calls the appropriate ones when needed.

Example:
class Number
{
   virtual void Output ();
};
class ComplexNumber
{
   virtual void Output ();
};