Pure virtual functions

Pure virtual functions provide a way to avoid defining base class member functions that have no meaningful implementation.

For much of this year, I've been explaining how virtual functions behave in C++ and how you can obtain similar behavior in C. Last summer, I showed 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).1 The vptr points to a table of function pointers called a vtbl (VEE-table). The past two months, I showed how to initialize the vptr in base and derived class objects.2, 3

This month, I'll explain the concept of pure virtual functions in C++. Although C doesn't provide native support for the concept, it's still applicable in C.

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 type hierarchy looks like:

The definition for the circle's area function is as you should expect:

double circle::area() const {
return pi * radius * radius;
}

The rectangle class derived from shape has members height and width instead of radius. The rectangle's area function is also as you should expect:

double rectangle::area() const {
return height * width;
}

What does shape's area function look like? For that matter, what's a shape that's neither a circle, nor a rectangle, nor any other type derived from base class shape?

Remember, the shape class represents a common interface for all shapes. For example, you can define a C++ function:

double volume(shape const *s, height h) {
return s->area() * h;
}

which computes the volume of a solid with a base whose shape is s and whose height is h. You can then write:

v = volume(&c, 4.2);

to compute the volume of a cylinder whose base is circle c and whose height is 4.2.

Alternatively, you can define volume's parameter s as a reference instead of a pointer, as in:

double volume(shape const &s, height h) {
return s.area() * h;
}

Then you don't need to take the address of volume's first argument explicitly, as in:

v = volume(c, 4.2);

You get the same result whether you use a pointer or a reference.

With the hierarchy of shapes, it's meaningful to use a "pointer to shape" or a "reference to shape" to refer to a circle, rectangle, or triangle, or possibly some other shape that you might derive from the base class shape. It's also meaningful to have a shape object that's the base class part of some derived class object. However, it's not meaningful to have a shape object that's just a shape.

A meaningful shape has a radius, or a height and a width, or some other attributes that define its physical extent. A shape that's just a shape has no such attributes. Nonetheless, declaring an area function in the shape class is a meaningful thing to do. That function declaration becomes part of the interface for every shape derived from the base class shape. That is, it's a way of forcing derived classes such as circle and rectangle to have an area function.

Although declaring an area function for shape is meaningful, defining it is not because the base class lacks the attributes it needs to compute its area. Pure virtual functions provide a way to avoid defining such functions that have no meaningful implementation.

In C++, you declare a virtual function as a pure virtual function simply by placing = 0 at the end of the function heading in the function declaration. For example, in:

the area and perimeter functions are now pure virtual functions. You need not define them for class shape.

If shape's area and perimeter functions are undefined, what happens when you try to call them? I'll explain that in my next column. I'll also look at what you have to do to approximate the same behavior in C.

Dan Saks is president of Saks & Associates, a C/C++ training and consulting company. For more information about Dan Saks, visit his website at www.dansaks.com. Dan also welcomes your feedback: e-mail him at dan@dansaks.com.