Channels ▼
RSS

C/C++

Initializing Derived Polymorphic Objects


You also need an explicit cast to convert a "pointer to derived" into "pointer to base," as in:

  circle c;
  ~~~
  f((shape *)&c); // pass a circle as a shape

This substitution works (f will compute the circle's perimeter correctly) only if the vptr has the same offset in the derived class object as it does in a base class object. The easiest way to satisfy this requirement is to implement each derived class type in C as a structure whose first member has the base class type, as in:

// circle.h – a C class for circle derived from shape

#ifndef CIRCLE_H_INCLUDED
#define CIRCLE_H_INCLUDED

#include "shape.h"

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

void circle_construct(circle *c, double r, color o, color f);

#endif

The base member of the circle structure above includes all the members inherited from the shape base class, including vptr. The definition for the circle_construct function appears, along with the circle vtbl object, in a separate source file:

// circle.c - circle implementation
~~~
#include "circle.h"

double circle_area(circle const *c) {
    return PI * c->radius * c->radius;
}

double circle_perimeter(circle const *c) {
    return 2 * PI * c->radius;
}

typedef struct circle_vtbl circle_vtbl;
struct circle_vtbl {
    double (*area)(circle const *);
    double (*perimeter)(circle const *);
};

static circle_vtbl the_circle_vtbl = {
    circle_area,
    circle_perimeter
};

void circle_construct(circle *c, double r, color o, color f) {
    shape_construct(&c->base, o, f);
    c->base.vptr = (shape_vtbl *)&the_circle_vtbl;
    c->radius = r;
}

The circle_construct function implements behavior comparable to a C++ constructor. It calls the shape_construct function to initialize the base class part. However, shape_construct sets the vptr to point to shape's vtbl, which is correct for the base class shape, but not for the derived class circle. Thus, circle_construct needs to reassign the vptr to point to circle's vtbl.

This assignment requires a cast because circle_vtbl isn't exactly the same type as shape_vtbl. The two structures have the same memory layout, but the corresponding pointers in the different structures point to functions with slightly different types.

Thus, the derived class constructor contains two assignments to the vptr. The second assignment completely overwrites the value assigned by the first. Ideally, the compiler will "optimize away" the first assignment. However, the compiler can do this optimization only if it can see both assignments in the context of the derived class constructor, which it can do only if the base class constructor is an inline function.

If you define the shape_construct function as inline, you should move the function definition to the shape.h header file. When you do that, you must also give the_shape_vtbl external linkage by removing the keyword static from its definition, as in:

// shape.c - a C base class for shapes

~~~

shape_vtbl the_shape_vtbl = { // used to be static
    shape_area,
    shape_perimeter
};

~~~

If your C compiler supports the inline keyword (from C99), then the shape.h header would look in part 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 *s);
    double (*perimeter)(shape const *s);
};

extern shape_vtbl the_shape_vtbl;

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

inline
void shape_construct(shape *s, color o, color f) {
    s->vptr = &the_shape_vtbl;
    s->outline = o;
    s->fill = f;
}

If your compiler doesn't support the keyword inline, then you can implement the constructor as a macro:

#define shape_construct(s, o, f) ( \
    (s)->vptr = &the_shape_vtbl, \
    (s)->outline = (o), \
    (s)->fill = (f) \
)

Either way, a compiler with a decent optimizer should eliminate the redundant assignment to the vptr.

Once again, you don't have to worry about any of this in C++. With C++, the compiler automatically generates code to initialize the vptr properly.


Dan Saks is president of Saks & Associates, a C/C++ training and consulting company. This article is based on a slightly different version that appeared on embedded.com, where Dan is a regular columnist.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video