OO Tips For The C Programmer Part I

By: Abdul Habra
Email:ahabra@yahoo.com
Version: 0.1
Date: July 1995

Note: This article was published originally in the Cap Gemini America - Cincinnati Unit Newsletter.

Introduction

Are you a 'C' programmer and want to use some of the Object Oriented (OO) programming techniques but don't have the time to learn C++ or C++ simply is not available in your programming environment, this article will show you how to use 'C' to get started. (Note: This article assumes that you already know 'C' and understand the basic OO concepts.)

Basic Concepts

This section provides a quick summary of some Object Oriented concepts and serves as a reminder of some 'C' constructs.

  • Encapsulation: To hide the internal implementation details of an object from the outside world.

  • Inheritance: To create a new class by adding new characteristics to a base class to form a derived class.

  • Polymorphism: Derived classes of a base class receive and respond to the same message, but their response may be different.

  • Function Pointer: Holds the address of a function of a certain type. e.g., int (*foo)() means that foo points to a function that returns an integer. Let's say we have the following function:

int square(int x) { return x*x; }

We could assign the address of square to foo:

foo = square;

Now if we have n as an integer, we can say:

n= foo(3);
/* n is 9 now */

Notice how calling foo() is equivalent to calling square(). Understanding the concepts of function pointers is important to understand the following sections.

Class Definition

To define a class just declare a typedef struct that contains all the members needed, e.g.,

typedef struct { ... } MyClass;

Methods are defined as function pointer class members. Let's define a simple class that stores a distance quantity in miles:

typedef struct {
  float DistanceMiles;
  float (*DistanceKm)(); /* get the distance in KiloMeters */
} DISTANCE;

Notice that DistanceKm is just a pointer to a function that returns a float.

Now you may wonder where the actual implementation of DistanceKm is, so let's define this function:

float DistanceKm(DISTANCE* this) {
  return this->DistanceMiles*1.609;
}

This function takes an argument that is a pointer to DISTANCE class and uses its DistanceMiles member to find the distance in Km. Notice that till now we didn't define any object (variable) of type DISTANCE because the class is just a declaration. In order to create an object of this class, just allocate some memory for it on the heap:

DISTANCE* ob= (DISTANCE*) calloc(1, sizeof(DISTANCE));

Additionally, we need to assign the function pointer member in ob to the address of DistanceKm() function:

ob->DistanceKm= DistanceKm;

Notice that allocating the memory and initializing the function pointer needs to be done for every object of the class once it is created. Sounds like a constructor, doesn't it? So let's create a constructor function that create and initialize an object of type DISTANCE and returns its address:

DISTANCE* ConstructDistance() {
  DISTANCE* ob= (DISTANCE*) calloc(1, sizeof(DISTANCE));
  ob->DistanceKm= DistanceKm;
  return ob;
}

You may wonder why didn't we make the ConstructDistance() a member of the DISTANCE class, and the answer is that we can't call a member function, i.e. method, before creating and initializing the class, which is what the function ConstructDistance() does.

Having a constructor that allocates memory on the heap calls for a destructor that will free this memory. The following is such a destructor:

void Destructor(DISTANCE** this) {
  free(*this);
  *this= 0;
}

The Destructor() is called after an object is created when it is no longer needed. This implies that the Destructor() can be a member function of the class defined like this:

void (*Destructor)();

Its address has to be initialized in the constructor:

this->Destructor= Destructor;

In order to call the Destructor():

ob->Destructor(&ob);

Next is the final definition of the class and its constructor:

typedef struct {
  float DistanceMiles;
  float (*DistanceKm)();
  void (*Destructor)();
} DISTANCE;

DISTANCE* ConstructDistance() {
  DISTANCE* ob= (DISTANCE*) calloc(1, sizeof(DISTANCE));
  ob->DistanceKm= DistanceKm;
  ob->Destructor= Destructor;
  return ob;
}

If you actually compile the above code you may need to put prototypes for the member functions -methods- before the constructor.

Conclusion

This article showed how to define a class, and create and destroy objects of this class. In a next article I will show how to implement encapsulation and how to define private and public members of a class. If there is interest I will show how to implement inheritance, polymorphism and templates. For those who want more challenging reading I could elaborate on how to make dynamic objects; i.e., objects that can have their definition changed at run-time. And for those who want more thought provoking tips, I could show how to distribute these objects; i.e., where objects of a class do not have to be in the same process or even on the same machine.

Acknowledgment

I would like to thank both of Jay Patel and Carol Viera for their ideas and help.

References
  1. Barakakati, Naba. (1991) Object Oriented Programming in C++. Indiana:SAMS.
  2. Stroutstrup, Bjarne. (1991) The C++ Programming Language, Second Edition. Addison-Wesley Publishing Company.
Comments