Object Oriented C: Constructors and Destructors

This is the first of a series of articles in which I’ll try to implement object-oriented functionality in C language.

What is an Object?

In OOP context, an object is a collection of data and functionality(some languages do away with the former, and say that a datum x can be seen as a pair of functionalities  getX and setX. We’ll only talk about the data part in today’s article.

What is a Class?

A class is a blueprint for creating objects. It tells the platform what data is contained in the instances of this class, and what functionality is provided by them. This is an example of a class:

data Name
data Age
functionality Eat

 

An obvious way to represent an object in C language is with a struct. Let’s try creating an IntArray class:

This is how we’ll construct and destroy instances of this class on the heap:

That doesn’t look very good, even though the implementation of IntArray contains only one memory resource. We want to be able to do something similar to C++, like this:

Let’s start by creating the constructor and destructor of IntArray:

These functions act as the source and sink of objects. But that’s not perfect. Ideally  new and delete should take care of the memory allocated for the struct IntArray, and the constructor and destructor of IntArray should take care of memory allocated to their members. These constructors and destructors shouldn’t actually construct or destroy objects. They should just act as the initializer and finalizer of the objects created by new and delete operators (to be implemented as functions in our code). Let’s apply this modification:

Now let’s get to the difficult part, implementing new and deletenew should allocate the memory for the new object, and then call the constructor to initialize it. delete should call the destructor and then deallocate the memory allocated in new (called create here, since my editor treats C files as C++):

Did you see a problem? create and destroy are calling the constructor and destructor of IntArray. What if we create another class, say, String. With the current structure, we’ll have to create another pair of create and destroy functions for them. The factoring we did turned out to be useless.

There is one thing we can do though. Let’s create another struct which contains all the resource management functionality specific to a class (which is just the constructor-destructor pair at this instant). Here it is:

size is the size of instances of this class, initializer and finalizer are the constructor and destructor of our class.

The implementation of create and destroy becomes:

This is what create does:

  1. takes a class and some other arguments
  2. allocates memory required for an instance of that class
  3. calls the initializer with the newly created instance and the rest of the arguments
  4. returns the newly created instance

destroy works similarly.

We need to make some modifications to the  IntArray class to work with this create, destroy pair:

This is how we’ll be using this code:

Looks good enough. Let’s test drive our framework by creating another class:

Works like a charm (i.e. doesn’t throw segfaults).

Corollary:

  • No type checking for our classes

The create and destroy functions are pretty generic, but that comes at the cost of complete type erasure. The user has to take care of the types associated with objects since they do not contain any type information inside them (we’ll see how we can fix that in later posts).

C is a weakly typed and strongly checked language, and our code does away with whatever type checking the compiler provides.

  • Boilerplate

Macros could help us a lot in this regard. Stay tuned for the followup posts.

This series is supposed to run parallel to the Object Oriented Programming course I’m taking this semester. As a test I’ll try to code a C++ program I wrote (dwmstatus) in C. The code I used in today’s article can be found here.

See you later 

4 thoughts on “Object Oriented C: Constructors and Destructors

  1. Hi Sagar – nice site and posts.

    I am not a fan of calling by looking up method names as strings. It may work with simple scripting but it does not scale up to complex system with large number of objects and large number of interactions – performance would be horrible.

    Another drawback to the stringy names is, as you noted, the lack of compiler checking of arguments and the associated lack of ability to offer auto-complete or method signature tips.

    Are you planning to introduce a fully prototyped class method (wrappers?) that would delegate to the typed class implementation? It’ll be interesting to see…

    1. Hi Dror, Thanks for the complement
      The goal was to lay out a method of doing traditional java like object-oriented programming in C, with minimal extra code, the article about methods was an offshoot from that, mainly because I can’t see any way of integrating the code used in that article with C types and structs.
      Implementing inheritance will be straightforward if we forget about multiple inheritance. But virtual method dispatching will require a lot of boilerplate if we don’t use string based method name resolution. It probably needs a preprocessor which transpiiles our code to C, maybe something similar to C with classes.

  2. BTW, in the example you gave:

    struct type {
    size_t size;
    void (*initializer)(void *self, va_list);
    void (*finalizer)(void *self);
    };

    void destroy(struct type class, void *obj) {
    class.finalizer(obj);
    free(obj);
    }

    The type parameter to destroy should by of type ‘struct type *’ and should not be passed by-val.

    1. It shouldn’t really matter whether we pass the type by value or by reference since it’s a read-only struct. Anyways, it’s probably better to embed a reference to type information in the object themselves.

Leave a Reply

Your email address will not be published. Required fields are marked *