lp0 on fire

My personal blog

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.