Object-oriented Programming in C (2017)

mooreds1 pts0 comments

Object-oriented Programming in C | Jordan LordJordan LordContact<br>Blog<br>Projects

Object-oriented Programming in C<br>1 May 2017

Implementing object-orientation in C is a fantastic way to understand how<br>object-oriented code works at the low level. Of course C++ exists to solve this<br>very problem. However, C++ arguably has its own problems. Top programmers, such<br>as Linus Torvalds, have spoken out<br>about how C++ is a "horrible language". It's easy to fall into the argument of<br>whether C++ is an incredibly powerful programming language, or whether it leads<br>to bad code. Quite frankly, I'm not qualified to say. The whole point of this<br>article is to demonstrate object-orientation in C. So we will avoid the flame<br>wars, for now.

Objects

Objects are the key data structures in object-oriented programming. C has the<br>struct data type to create, what basically are, objects.

Here is an example:

// Declaration<br>typedef struct RectangleTag Rectangle;

struct RectangleTag {<br>float width;<br>float height;<br>};

// Instantiation<br>Rectangle rect;

rect.width = 4;<br>rect.height = 2;

As you can see, this behaves very similar to an object in other object-oriented<br>programming languages, such as Java. Let's just call it an object, for ease of<br>understanding. However, there are a few problems. For it to be object-oriented,<br>we'll need methods that are assigned to the object. Let's start by creating a<br>"constructor" function.

Constructors and Destructors

A constructor is a type of function that is specifically used to create an<br>object. In Java and many other object-oriented programming languages, you invoke<br>the constructor with the new keyword. In the previous example, we created an<br>object that was allocated to the stack. This isn't helpful. The details aren't<br>important as to why, for now. Instead we are going to allocate the object on the<br>heap. Then a pointer to that object will be returned from the constructor<br>function. The C standard library has a function for allocating memory on the<br>heap; it's called malloc.

Here is an example of a constructor function for Rectangle:

#include

// Constructor<br>Rectangle *Rectangle_new(float width, float height)<br>// Allocate memory to object<br>Rectangle *self = malloc(sizeof(Rectangle));

// Default parameters<br>self->width = width;<br>self->height = height;

// Return pointer to object<br>return self;

Creating an instance of Rectangle using the constructor function:

Rectangle *rect = Rectangle_new(2, 2);

When you're allocating memory on the heap, it isn't deallocated when you go out<br>of function scope, like it does for objects allocated on the stack. Therefore, a<br>"destructor" function has to be created, in order to deallocate the object. The<br>C standard library has a function for cleaning up memory allocated on the heap;<br>it's called free. As shown below:

#include

// Destructor<br>void Rectangle_destroy(Rectangle *self)<br>// Free the memory<br>free(self);

Although it may seem pointless to have a destructor function if free is just<br>going to be called, it is important to have a destructor function because<br>objects may need to clear up more objects that are on that object, if that makes<br>sense?

Methods

Methods are very important to object-oriented programming. In Java and many<br>other object-oriented languages, methods associated with an object reference the<br>object with the this keyword. In C++, the object's properties are all in the<br>method's scope. In C, we are going to take the Java approach as it is easiest to<br>implement. A slight tweak: we'll use self instead of this, since this is a<br>reserved keyword in C++, which may harm cross compatibility.

To do this, our function's first parameter will take a pointer to its object.<br>Below is a sample method for calculating the Rectangle's area.

float Rectangle_get_area(Rectangle *self)<br>return self->width * self->height;

This is used as below. The area variable should be equal to 4.

Rectangle *rect = Rectangle_new(2, 2);

float area = Rectangle_get_area(rect);

Just for reference, here is an example of a method for calculating the<br>perimeter.

float Rectangle_get_perimeter(Rectangle *self)<br>return (self->width * 2) + (self->height * 2);

There are a few issues with this. The main issue is it is not polymorphic. We<br>don't have a method that can be called for all Shapes to calculate that<br>Shape's area. On another Shape, the width and height properties may not<br>exist; the method would fall over when it was called with a Shape.

Dynamic Methods

There are a few issues with our previous method of implementing methods. There<br>is no method dispatching. No way to decide what method to call depending on the<br>type of object. We have to do that manually. However, polymorphism is achievable<br>in C. We must use function pointers on the structs themselves.

Let's redeclare our Rectangle struct to have the methods declared on the<br>struct itself:

typedef struct RectangleTag Rectangle;

struct RectangleTag {<br>float width;<br>float height;

float (*get_width)(Rectange *self);<br>float (*get_perimeter)(Rectange...

object rectangle function float oriented width

Related Articles