People that have always programmed in an object oriented language sometimes don't have a clear idea on the underlying mechanisms of inheritance and polymorphism. It also happend that people that have always programmed in a procedural language tend to think that they need an object oriented language to program in an object oriented way.
The large majority of experienced C programmers know how to program in C in an object oriented way, like it is possible to see in many open source projects. The problem is that it is not always easy to extrapolate a concept from a big and mature software project.
This is why I have written a little example program to explain how to program in C in an object oriented way. It has been a good exercise for me and I hope it will be also useful to other people.
main.c
#include <stdio.h> #include "shape.h" #include "square.h" #include "circle.h" #define NUM_SHAPES 2 int main(int argc, char **argv) { int i; Shape shape[NUM_SHAPES]; shape[0] = (Shape) new_Circle(4, 4, 2); shape[1] = (Shape) new_Square(9, 4, 2); for (i = 0; i < NUM_SHAPES; i++) { shape[i]->draw(shape[i]); } for (i = 0; i < NUM_SHAPES; i++) { shape[i]->destroy(shape[i]); } return 0; }
Standard Output
CIRCLE_C: Creating a new Circle SQUARE_C: Creating a new Square CIRCLE_C: Drawing a Circle of radius 2 in (4,4) SQUARE_C: Drawing a Square of length 2 in (9,4) SHAPE_C: Destroing a shape SHAPE_C: Destroing a shape
shape.h
#ifndef _SHAPE_H_ #define _SHAPE_H_ #define SHAPE int x_center; \ int y_center; \ void (*draw)(Shape this); \ void (*destroy)(Shape this) typedef struct Shape_struct* Shape; struct Shape_struct { SHAPE; } Shape_struct; void destroy(Shape this); #endif
shape.c
#include <stdio.h> #include <stdlib.h> #include "shape.h" void destroy(Shape this) { printf("SHAPE_C: Destroing a shape\n"); if (this != NULL) free(this); }
square.h
#ifndef _SQUARE_H_ #define _SQUARE_H_ #include "shape.h" typedef struct Square_struct* Square; struct Square_struct { SHAPE; int length; } Square_struct; Square new_Square(int x_center, int y_center, int length); #endif
square.c
#include <stdio.h> #include <stdlib.h> #include "square.h" static void draw(Shape this) { Square thisSquare = (Square) this; printf("SQUARE_C: Drawing a Square of length %d in (%d,%d)\n", thisSquare->length, thisSquare->x_center, thisSquare->y_center); } Square new_Square (int x_center, int y_center, int length) { Square square; printf("SQUARE_C: Creating a new Square\n"); square = (Square) malloc(sizeof(Square_struct)); square->x_center = x_center; square->y_center = y_center; square->length = length; square->draw = draw; square->destroy = destroy; return square; }
circle.h
#ifndef _CIRCLE_H_ #define _CIRCLE_H_ #include "shape.h" typedef struct Circle_struct* Circle; struct Circle_struct { SHAPE; int radius; } Circle_struct; Circle new_Circle(int x_center, int y_center, int radius); #endif
circle.c
#include <stdio.h> #include <stdlib.h> #include "circle.h" static void draw(Shape this) { Circle thisCircle = (Circle) this; printf("Circle_C: Drawing a Circle of radius %d in (%d,%d)\n", thisCircle->radius, thisCircle->x_center, thisCircle->y_center); } Circle new_Circle (int x_center, int y_center, int radius) { Circle circle; printf("CIRCLE_C: Creating a new Circle\n"); circle = (Circle) malloc(sizeof(Circle_struct)); circle->x_center = x_center; circle->y_center = y_center; circle->radius = radius; circle->draw = draw; circle->destroy = destroy; return circle; }
It is important to master how each part of the code will use the memory resources of your system.
The behavior of each object, the methods implementation, will be loaded on the static memory.
The instances of each object, the structure memory area (variables and pointer to functions), will be allocated in three possible memory area, depending on how they are declared in the code:
The following graph gives an idea of this distinctions: