Copyright © Quantum Leaps, LLC. All Rights Reserved.
1 Introduction
Object-oriented programming (OOP) is not the use of a particular language or a tool. It is rather a way of
design based on the three fundamental design meta-patterns:
• Encapsulation – the ability to package data and functions together into classes
• Inheritance – the ability to define new classes based on existing classes in order to obtain reuse and
code organization
• Polymorphism – the ability to substitute objects of matching interfaces for one another at run-time
Although these meta-patterns have been traditionally associated with object-oriented languages, such as
Smalltalk, C++, or Java, you can implement them in almost any programming language including portable
ANSI-C
[1,2,3,4,5,6]
.
If you develop end-user programs in C, but you also want to do OOP, you probably should be using
C++ instead of C. Compared to C++, OOP in C can be cumbersome and error-prone, and rarely
offers any performance advantage.
However, if you build or use application frameworks, such as the the QP/C and QP-nano active
object frameworks, the OOP concepts are very useful as the primary mechanisms of customizing,
specializing, and extending the frameworks into applications. In that case, most difficulties of doing
OOP in C can be confined to the framework and can be effectively hidden from the application
developers. This Application Note has this primary use case in mind.
This Application Note describes how OOP is implemented in the QP/C and QP-nano active object (actor)
frameworks. As a user of these frameworks, you need to understand the techniques, because you will
need to apply them also to your own application-level code. But these techniques are not limited only to
developing QP/C or QP-nano applications and are applicable generally to any C program.
2 Encapsulation
Encapsulation is the ability to package data with functions into classes. This concept should actually
come as very familiar to any C programmer because it’s quite often used even in the traditional C. For
example, in the Standard C runtime library, the family of functions that includes
fopen()
,
fclose()
,
fread()
, and
fwrite()
operates on objects of type
FILE
. The
FILE
structure is thus encapsulated
because client programmers have no need to access the internal attributes of the
FILE struct
and
instead the whole interface to files consists only of the aforementioned functions. You can think of the
FILE
structure and the associated C-functions that operate on it as the
FILE
class. The following bullet
items summarize how the C runtime library implements the
FILE
class:
1. Attributes of the class are defined with a C
struct
(the
FILE struct
).
2. Operations of the class are defined as C functions. Each function takes a pointer to the attribute
structure (
FILE *
) as an argument. Class operations typically follow a common naming convention
(e.g., all
FILE
class methods start with prefix
f
).
3. Special functions initialize and clean up the attribute structure (
fopen()
and
fclose()
). These
functions play the roles of class constructor and destructor, respectively.
You can very easily apply these design principles to come up with your own “classes”. For example,
suppose you have an application that employs two-dimensional geometric shapes (perhaps to be
rendered on an embedded graphic LCD). The basic
Shape
“class” in C can be declared as follows:
1 of 13