Problems with inheritance by composition

July 09, 2013

Dan_Saks-July 09, 2013

I'm continuing my discussion of how to implement and use virtual functions in C. The basic approach is to implement each class as a C structure and implement each associated method as a function whose first parameter is a pointer to the structure. For each polymorphic class (a class with at least one virtual function), the corresponding C structure has a member called a vptr (VEE-pointer), which points to a table of function pointers called a vtbl (VEE-table).1

Virtual functions are useful only in inheritance hierarchies--collections of classes where some are derived from others. A few of months ago, I showed two distinct ways to mimic inheritance in C, which I call "inheritance by composition" and "inheritance by copying and editing."2 Although the mechanics of inheritance by composition might be the simpler of the two, I generally prefer inheritance by copying and editing because it yields a better user interface. This month, I'll explain some problems with inheritance by composition that make this technique undesirable.

As in prior articles, my sample classes represent an assortment of two-dimensional geometric shapes such as circle, rectangle, and triangle, all derived from a common base class called shape. In C, the shape "class" implementation requires two structures: one for the shape data members and one for the corresponding shape vtbl. The C declarations for the shape class look like:

// shape.h - a C base class for shapes
~~~

typedef struct shape shape;
typedef struct shape_vtbl shape_vtbl;

struct shape_vtbl {
    double (*area)(shape const *me);
    double (*perimeter)(shape const *me);
};

struct shape {
    shape_vtbl *vptr;
    color outline, fill;
};

void shape_construct(shape *me, color o, color f);
double shape_area(shape const *me);
double shape_perimeter(shape const *me);
 

The function declarations at the end of the header declare all of the shape member functions, even the virtual ones, as non-virtual functions. Each call to a member function (whether virtual or non-virtual) acts upon a specific "target" object. Each member function has a parameter named me, which the function uses to access its target.

Using "inheritance by composition," you declare the circle structures in the circle.h header as:

typedef struct circle circle;
struct circle {
    shape base;         // the base class subobject
    double radius;
};

typedef struct circle_vtbl circle_vtbl;
struct circle_vtbl {
    shape_vtbl base;    // the base class vtbl
    // virtual functions introduced in circle
};

In the circle.c source file, you define the_circle_vtbl (the circle vtbl object) and initialize its members. Unfortunately, given the declarations thus far, this straightforward initialization won't compile:

circle_vtbl the_circle_vtbl = {
    circle_area,        // initializes base.area
    circle_perimeter    // initializes base.perimeter
};

Let me explain why.





1. Saks, Dan, "Virtual functions in C," Embedded.com, August 8, 2012. www.embedded.com/4391967.

2. Saks, Dan, "Alternative idioms for inheritance in C," Embedded.com, April 1, 2013. www.embedded.com/4411013.

< Previous
Page 1 of 3
Next >

Loading comments...

Most Commented

Parts Search Datasheets.com

KNOWLEDGE CENTER