From C to C++

Introduction

Making the transition from C programming to C++ programming is not just a simple
matter of learning off a few extra keywords (and there are surprisingly few of them).
The main labour of the transition is learning to think in an object oriented way.

As a C programmer you are probably familiar with the idea of functional decomposition.
The idea behind this is that when you design and implement a program, you most likely
subdivided the programming effort by breaking the work of the program into a set of
key functions, which may themselves in turn call on other functions.  This divide and conquer
approach to programming is very powerful as it allows the programmer to concentrate on
small, easily devised and debugged subroutines.  Even today, with the rise of object oriented
programming, this approach is still very useful in small to medium sized programming endeavors
as it closely follows the way people deal with large complex tasks.

The design and implementation of programs using a functional approach alone begins to become
problematic as the size and complexity of the program increases.  Consider the following scenario:
A company is 80% through a software development project when the client (or some other factor)
introduces a change which requires a significant change in a key data structure used throughout
the program.  As a result, every function which makes use of strucures of this type must be checked
for compatibility and fixed if necessary.  The problem?  The functions are designed with a particular
data model in mind.  If the model changes so too must the functions.  The same problem will also arise
if the company decides to release Version 1.2 of the program which includes extra features.  This is
where Object Oriented programming (OOP) steps in.

There are three basic principles associated with OOP:

OOP is possible in many different languages  : and not necessarily just the Object Oriented ones as
it is possible to write an object oriented program even in assembly language.

Encapsulation.

Encapsulation could go a long way towards solving the problem in the scenario described in the
introduction.  Through the mechanism of encapsulation, a programmer hides aspects of the
underlying program data model behind a set of 'gateway' functions.  If a change takes place in
the data model, it can often be hidden from the main body of the functions by simply changing the
way the 'gateway' functions are coded.  The implementation of a watch provides a good example
of encapsulation.  You, as the user of the watch determine the current time by reading the face of
the watch (gateway output function for the watch).  You can adjust the time displayed on the clock
face by moving the hands with the winder at the side (gateway input function).  Does it matter to you
how the watch is implemented inside?  From the point of view of telling the time the answer is of
course no.  The functionality of the watch is completely described by the two gateway functions which
encapsulate its actual implementation.
 

Same functionality: two implementations, one interface.

Polymorphism & Inheritance

Polymorphism is a bit like antother aspect of  encapsulation.  Suppose you are writing a have a routine which takes
a long time to run and, to re-assure the user, you decide to display some sort of progress indicator on screen.  You
could do this in a number of ways.  You could for example, include code in the long routine which outputs a text
display of its current progress.  This is all very well if you program will only ever use a text output terminal.  If you
switch to some graphical user interface, you would have to re-code part of the long routine.  A further disadvantage
of this approach is that you would probably have to ask whoever writes the long routine to write the user interface
as well.  This may not be viable or desirable.
An object oriented approach to this problem could be outlined as follows:
    1) Create a progress indicator object with a "well-known" interface
    2) Pass this object as a parameter to the long routine
    3) The long routine interacts with the progress indicator object using the "well-known" interface
        to send progress information to the screen.
This approach has a number of advantages associated with it. The task of creating the user interface is separated
from the other tasks performed within our long function.  Sub-dividiing problems in this manner usually speeds their
solution.  A user interface expert may be used to code the progress indicator object and a data processing expert
(if appropriate) may be used to code the remainder of the long function (Horses for courses advantage).   The long
function is not tied to any particular implementation of the progress indicator which of course may be useful if your
user interface changes.
The mechanism by which this is achieved is Inheritance (with a spinkling of polymorphism).  Download the following
sample code (for Visual C++ 5) and we will examine it more closely.
 

Progress1: An example of Inheritance & Polymorphism at work.

The C++ files within this project are main.cpp and screen.cpp.  The screen.cpp file can be ignored for the moment.
It contains a collection of routines for screen management which will be of use to us in this and in several other programs.
The principal file is main.cpp.  A class hierarchy is declared within this file as follows:

The lower three classes are specialised versions of a generalised Progress class called CProgress (the notation
here is Unified Modelling Language:- UML).  These specialised classes differ in the way they display their output therefore they
each have their own version of Show which writes data to the screen.  When the program is executed, the function
"SomeLongProcess" is called and passed the address of three objects (derived from CProgress) with which it interacts.  The
"long process" calls upon the various methods in the CProgress object including the Show method.
Lets take a close look at this "SomeLongProcess" routine:

void SomeLongProcess(CProgress *prog)
{
 for (int i=0; i < 11 ; i++)
 {
  cls();
  prog->SetPercent(i*10);
  prog->Show(); // as if by magic, the correct version of show is called each time
       // polymorphism in action.
  printf("\nPress a key\n");
  _getch();

 }
}

The declaration implies that the routine will accept only the address of a CProgress object.  Under C++ however,
it is also legal to pass the address of any object which is an instance of a class derived from CProgress.  This  makes
perfect sense; afterall, a CTextProgress object is a sort of CProgress object.  (It is not legal however to use the
class hierarchy in the opposite way.  Suppose you write a routine which is passed the address of a CTextProgress
object.  In this instance it would not be legal to pass the address of a CProgress object or indeed a CTextBarProgress
object nor a CGraphicProgess object: only objects which are specialised versions of CTextProgress are permitted).
During the course of SomeLongProgress, a call is issued to Show.  When you run the program, you will notice that
the correct version of Show is called in each case even though there seems to be no code to support this.  What's
going on?  Polymorphism.  The same line of code

        prog->Show();

produces three different results.
How is it that the computer knows which version of Show to call?  There must be some information in the "prog"
object which the computer can read and therefore figure out the correct version of Show to call.  Well, there is.  You
will notice that the Show function is declared as "virtual" in CProgress and its derived classes.  This keyword causes the
compiler to generate code in such a way that the address of the correct version of Show is included in each instance of
CProgress (and derived) classes.  The compiler also generates code which looks up this address at each call to Show.
This is the mechansim by which polymorphism is achieved.  If there are several virtual functions in a class, the compiler
generates a lookup table of these function addresses and attaches it to each instance of that class.  This table of function
address is known as a V-table.
 
 

The C++ approach to object oriented programming.

The C language was extended to facilitate object oriented programming.  This extension included, amongst other
things some new keywords.  The following is a summary of the ones we have met so far.

class                Used when declaring a class 'type', similar to the keyword 'struct'.  e.g.
            class MyClass {
                            .
                            .
                           .
            }; // DON'T FORGET THE TRAILING SEMICOLON!
                        This declares a new class type.  Classes, unlike structs can contain functions (methods)
                        as well as variables (attributes).

public:               When used inside a class, in advance of the declaration of some variable or method,
                          this keyword specifies that the variable or method is to be accessible to other functions
                          within your program (outside of the class).
                          e.g.
             class MyClass {
             public:
                 int i;
                 int GetValue();
                 .
                 .
             }
                          The above would enable other functions within your program (e.g. main) to directly
                          access and modify i and to call GetValue.

                          When public preceeds the name of a base class, this implies that the public and protected
                          members of the base class will become public and protected members of the derived class.
                          e.g.
             class MyOtherClass : public MyClass {
             }
                          i and GetValue will appear, to the rest of the program, to be public members of MyOtherClass.
 

private:              The private keyword locks away variable and methods inside a class in such a way that only
                           functions which are members of that class can access them.  It is used in a similar manner to
                           'public'.  You may wonder why you would want to hide variables and methods in such a manner.
                            The answer lies in Encapsulation.  By using 'private' you can ensure that other parts of your
                            program do not rely on a particular implementation approach inside a class.  You can also
                            use private to stop other parts of your program from meddling with the contents of some critical
                            variable within your class (such as a pointer or a file handle).

protected:           The protected keyword is very similar to private in that it is used to obscure implementation and
                            protect sensitive program areas.  'protected' is a less draconian form of private in that it will allow
                            classes derived from the one you are working on to access protected variables and methods.

virtual:                   As stated above in the Progress1 example, the virtual keyword is used to force the compiler to
                            insert code which implements late binding.  Late binding is the process where a functions address
                            is determined at run-time rather than at link time.  You typically declare virtual functions in the following
                            manner
                            class MyClass {
                            public:
                                            virtual void SetValue(int NewValue);
                            }