OO Tips For The C Programmer - Part I
- Author: Abdul Habra
- Version: 0.1.1
- Date: July 1995
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 thatfoo
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 ofsquare
tofoo
:foo = square;
Now if we haven
as an integer, we can say:n= foo(3); /* n is 9 now */
Notice how callingfoo()
is equivalent to callingsquare()
. Understanding the concepts of function pointers is important to understand the following sections.
Class Definition
To define a class just declare atypedef 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
- Barakakati, Naba. (1991) Object Oriented Programming in C++. Indiana:SAMS.
- Stroutstrup, Bjarne. (1991) The C++ Programming Language, Second Edition. Addison-Wesley Publishing Company.