For much of this year, I've been discussing polymorphic types and virtual functions. Last month, I explained the memory layout of polymorphic objects. I also previously shown how to emulate a polymorphic C++ class (a class with at least one virtual function) as a C structure that has an additional member commonly called a vptr (VEE-pointer). The vptr points to a table of function pointers called a vtbl (VEE-table). This month, I'll look at initializing derived class objects.
As in my 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. The C++ definition for the shape base class looks in part like:
class shape {
public:
shape(color o, color f); // constructor
virtual double area() const;
virtual double perimeter() const;
private:
color outline, fill;
};
In C, the comparable declarations look like:
// shape.h - a C base class for shapes
#ifndef SHAPE_H_INCLUDED
#define SHAPE_H_INCLUDED
typedef struct shape shape;
typedef struct shape_vtbl shape_vtbl;
struct shape_vtbl {
double (*area)(shape const *s);
double (*perimeter)(shape const *s);
};
struct shape {
shape_vtbl *vptr;
color outline, fill;
};
void shape_construct(shape *s, color o, color f);
double shape_area(shape const *s);
double shape_perimeter(shape const *s);
#endif
As I showed last month, you can define the shape vtbl object in a source file that also defines the member functions of the shape "class":
// shape.c - a C base class for shapes
#include "shape.h"
~~~
static shape_vtbl the_shape_vtbl = {
shape_area,
shape_perimeter
};
void shape_construct(shape *s, color o, color f) {
s->vptr = &the_shape_vtbl;
s->outline = o;
s->fill = f;
}
In C++, the definition for a circle class derived from shape looks like:
class circle: public shape {
public:
circle(double r, color o, color f); // constructor
virtual double area() const;
virtual double perimeter() const;
~~~
private:
double radius;
};
Derivation defines an "is a" or "is a kind of" relationship between the derived and base class. That is, it lets you substitute a derived class object, such as a circle or rectangle, for a base class shape object. For example, given a C++ function such as:
void f(shape *p) {
~~~
p->perimeter(); // virtual call to shape's perimeter
~~~
}
you can pass it a derived class object, as in:
circle c; ~~~ f(&c); // pass a circle as a shape
and it computes the circle's perimeter correctly. With a little more effort, you can emulate this behavior in C. In C, you have to explicitly mention the vptr in virtual function calls, as in:
void f(shape *p) {
~~~
p->vptr->perimeter(p); // virtual call to shape's perimeter
~~~
}


