OO Tips For The C Programmer - Part II
- Author: Abdul Habra
- Version: 0.1.1
- Date: October 1995
Introduction
This is the second article in a series on Object Oriented programming techniques using C. In the previous
installment of this series of articles I explained how to use C to define a class. The
DISTANCE
class was used as an example. In this installment
encapsulation will be presented including how to define public and private members of a class.
Encapsulation is the ability to hide the internal implementation details of an object from the outside world.
Basic Concepts
This section provides a brief explanation of some C constructs that will be used later to support encapsulation.Static Functions
A static function is a function that is only visible in the file that contains it. A static function is defined
by adding the wordstatic
at the beginning of the function definition. For example, if we have a
file called test1.c
that has the function:
static int square(int x) { return x*x; }then the only functions that can use
square()
are functions defined in the file
test1.c
.
Casting
Casting provides the ability to convert a value to a different type. To cast an expression, put the target data type in parentheses directly before the expression. For example:float x; x= (float) 1;assigns the
int
value 1 to thefloat
variable x
.
In the previous installment we used casting in the constructor:
DISTANCE* ob=(DISTANCE*) calloc(1, sizeof(DISTANCE));
Thecalloc()
function returns avoid
pointer, and we cast it to
DISTANCE
pointer in order to assign this value to
ob
without the compiler generating any warning messages.
In summary, if we have the cast:(MyType)MyValue
we will be telling the compiler
to treat MyValue
as if it is of type MyType
.
Encapsulation
We need to have a facility in which the methods of a class can only be accessed through the class. Additionally, the class may have members that should not be visible outside the class definition, i.e. private.Methods Scope
For a class, only its definition should be visible by the class' users. The implementation of the class should be completely hidden. The solution is simple:- Put the class definition in a header file
FileName.h
- Put a prototype for the constructor in the header file.
- Put the class implementation in a source file
FileName.c
- Define all the methods (except the constructor) in the source file to be static.
- Now, whenever you want to use the class, include its header file.
For ourDISTANCE
class,
put the class definition (i.e. struct
) indistance.h
,
put the implementation of its methods in distance.c
and make all the methods static (except ConstructDistance
). For example:
static float DistanceKm(DISTANCE* this) { return this->DistanceMiles*1.609; }
Private Members
Although the class implementation is hidden from its users, all the class members are accessible by anyone who this class. We need to have a mechanism in which some of the class's members are inaccessible by the class users. In ourDISTANCE
class, suppose we want to be able to set and
get the distance in both units of Miles or Kilometer.
- To start, define the class definition as follows:
distance.h
typedef struct { void* private; void (*Destructor)(); void (*SetMiles)(); float (*GetMiles)(); void (*SetKm)(); float (*GetKm)(); } DISTANCE; DISTANCE* ConstructDistance();
Notice that there is no data member to store the distance value in the class definition. Additionally, there is avoid
pointer namedprivate
. - Create the following type in
distance.c
typedef struct { float DistanceMiles; } PrivateType;
Put in thisPrivateType
all the class members that you wish to make private. - In the constructor, assign the class member
private
to a memory allocated type,PrivateType
:ob->private= calloc(1, sizeof(PrivateType));
- Update the destructor to free
private
:free((*this)->private);
- Methods of the class can access
DistanceMiles
as a member in a castedprivate
, for example:static void SetMiles(DISTANCE* this, float miles) { PrivateType* p= (PrivateType*)this->private; p->DistanceMiles= miles; }
Notice howprivate
-which is a void pointer- is casted to aPrivateType
pointer. - Remember to have
DISTANCE* this
as the first argument of every method. - If anybody uses the
DISTANCE
class, they can not access the contents ofPrivateType
because it is defined indistance.c
- Although the class has a public member
private
, this member is only a void pointer. The user of the class has no way of accessing the contents of this private without accessing the source filedistance.c
- The only discipline required from the class user is not to change the value of
private
after the class construction.
Conclusion
It is shown here that with utilizing some standard C constructs we are capable of supporting encapsulation. This is achieved with a simple approach. Mainly, define all methods implementation to be static and have a class member that the user can not change (private). In the next installment of this series of articles, I will discuss inheritance and polymorphism then see if we could implement them using C.Acknowledgment
I would like to thank both of Carol Viera and Srinivasa Komatineni for their review and suggestions.References
- Stroutstrup, Bjarne. (1991) The C++ Programming Language, Second Edition. Addison-Wesley Publishing Company.
- Darnell, Peter A. and Margolis, Philip E. (1988) Software Engineering in C. Springer-Verlag New York Inc.