C89-OOP: Dynamic allocation
In most OOP languages a class is a reference type, meaning they get allocated on the heap. This means in C89 that we got to manually allocate and deallocate the memory for this class.
New Foo
For a recap, here is Foo
from C89-OOP: Class
that is going to be used for demonstration purposes:
typedef struct Foo Foo;
struct Foo
{
int x;
};
void Foo_ctor(Foo* self, int x)
{
self->x = x;
}
Adding in a wrapper around memory allocation isn't hard by any means:
Foo* Foo_new(int x)
{
Foo* self = malloc(sizeof(Foo));
Foo_ctor(self, x);
return self;
}
Here we allocate the memory, call the constructor and return the instance to match the behaviour of the new
keyword in most OOP languages. Initializing the base classes can be done in the constructor, no need to allocate this explicitly:
void Bar_ctor(Bar* self, int x, int y)
{
Foo_ctor(self->base, x);
self->y = y;
}
Bar* Bar_new(int x, int y)
{
Bar* self = malloc(sizeof(Bar));
Bar_ctor(self, x, y);
return self;
}
As for deallocation:
void Foo_dtor(Foo* self)
{
/* deallocate member memory */
}
void Foo_delete(Foo* self)
{
Foo_dtor(self);
free(self);
}
While it's not required, I think it's nice to wrap the free
in a new function to make it distinct that we're deallocating an instance of Foo
and not something else by chance. One thing to keep in mind is that you want to make sure to deallocate all allocated memory from inherited classes as well:
void Bar_dtor(Bar* self)
{
Foo_dtor(self->base);
/* deallocate member memory */
}
void Bar_delete(Bar* self)
{
Bar_dtor(self);
free(self);
}
This ensures we release all memory.
Usage
Again, nothing noteworthy:
Foo* foo = Foo_new(1);
int val = 0;
val = foo->x;
Foo_delete(foo);
Conclusion
A simple but effective stratagy for a simple problem. One thing I like about this is how easy it is to read. You know exactly what everything is doing without much overhead. In fact, it can be expanded on by adding support for memory pools without too much complications.