Data Hiding in C
Object-oriented programming languages are described as supporting encapsulation, polymorphism, and data hiding. They provide powerful features that allow software components to be designed and implemented for change.
Observing the mechanisms in an object-oriented programming language can potentially lead to a parallel implementation in a procedural language. This allows some object-oriented design knowledge that has been refined through study and experience to be implemented in procedural languages such as C.
A Circle type is a typical classroom example of a user-defined data type. Getting an area or circumference are common operations performed on instances of a Circle type. In C++, a Circle type may be defined as follows:
class Circle
{
double radius;
public:
Circle()
{
// not using init list here to make parallelism
// with example C code more easily seen
this->radius = 0;
}
void setRadius( double r )
{
this->radius = r;
}
double getRadius()
{
return this->radius;
}
double getCircumference()
{
return 2 * PI * this->radius;
}
double getArea()
{
return PI * pow( this->radius, 2 );
}
};
Member functions, which are functions that are called to operate on objects, in object-oriented languages tend to have a reference or pointer, which may be implicit or explicit, to the object on which the function operates. The use of a struct and functions that accept a pointer to the struct instances provides similar behavior in C. C code that mimics the example C++ code is presented here:
typedef struct
{
double radius;
} Circle;
void circle_construct( Circle *c )
{
c->radius = 0;
}
void circle_setRadius( Circle *c, unsigned r )
{
c->radius = r;
}
double circle_getRadius( Circle *c )
{
return c->radius;
}
double circle_getCircumference( Circle *c )
{
return 2 * PI * this->radius;
}
double circle_getArea( Circle *c )
{
return PI * pow( this->radius, 2 );
}
An association between the functions and the data, which is accomplished through encapsulation in the C++ code, is made in the C code through a coding convention. The convention here directs the form of the function signature to be as follows:
returnType typeName_operationName( params )
Data hiding is accomplished by another coding convention. Instead of dependents on Circle objects operating directly on the Circle objects’ fields, the dependents shall only call functions that manipulate the object on behalf of the dependents.
Code that is dependent on Circle objects as implemented in C is presented below:
Circle c;
circle_init( &c );
circle_setRadius( &c, 3 );
printf( "%f", circle_getRadius( &c ) ); /* "3.0" */
printf( "%f", circle_getCircumference( &c ) ); /* "18.85" */
printf( "%f", circle_getArea( &c ) ); /* "28.27" */
circle_setRadius( &c, 4 );
printf( "%f", circle_getRadius( &c ) ); /* "4.0" */
printf( "%f", circle_getCircumference( &c ) ); /* "25.13" */
printf( "%f", circle_getArea( &c ) ); /* "50.26" */
The above code is more insulated from change than code that accesses the Circle fields directly. A field for the circle’s color and functions that operate on that field can be introduced, for example, and the above code has a good chance of not requiring modification while having its behavior remain unchanged. Ignoring the possibility of floating-point approximation errors, as another example, the internal representation of the Circle type can be overhauled completely by changing the radius field into a field containing the circumference of a circle and modifying the associated functions appropriately, and a necessity of change in the above code remains unlikely. The ability to change the representation of a data type without modification to dependent code evidences this technique’s effectiveness in making code resilient to change.
Parallels to the idioms and adages of languages like C++ can be implemented in C. By mimicking the natural mechanisms of object-oriented languages. the benefits of those features can be introduced in procedural languages where such mechanisms are neither directly supported nor idiomatic.
July 26th, 2013 at 7:07 pm
2009, 2010, 2011…oops missing 2012; here’s 2013:
Answering the critics, ‘radius’ remains private if the member functions are encapsulated in a separate module (file), and the struct declared only within that module. The functions have access, but the radius’ scope does not include main(). Usually an .h file would list the function prototypes to share with the module containing main.
February 2nd, 2011 at 7:26 am
what if in main function i wrote :
Circle *ci;
ci->radius=6;
so data hiding is not implemented correctly
April 7th, 2010 at 11:48 pm
i want a program iwhich shows hat private data mebebrs of a class can not be accessed
November 4th, 2009 at 1:16 am
I was looking how data hiding was done procedural languages and this explained it really well. Nice blog.