Implementing pure virtual functions
Implementing pure virtual functions as null pointers is simple but probably not robust enough for most applications.
Last year, I wrote several columns on how virtual functions behave in C++ and how you can obtain similar behavior in C. In short, you can 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).
Last month, I introduced the concept of pure virtual functions in C++.[2] This month, I'll show how compilers typically implement pure virtual functions by showing how you can achieve similar behavior 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 C++ definition for the shape
base class looks in part like:
class shape {public: shape(color o, color f); // constructor virtual double area() const = 0 ; virtual double perimeter() const = 0 ; ~~~ private:< color outline, fill;};
The characters = 0
appearing at the end of the declarations for area
and perimeter
indicates that they are pure virtual functions. They need not be defined. As I asked at the end of my previous column, if shape
's area
and perimeter
functions are undefined, what happens when you try to call them?
[1] Saks, Dan, "Virtual functions in C," Embedded.com, August 8, 2012. www.embedded.com/4391967.
[2] Saks, Dan, "Pure virtual functions," Embedded.com, December 12, 2012. www.embedded.com/4403313.
C++ makes calling a pure virtual function nearly impossible. A class with at least one pure virtual function is called an abstract class . C++ doesn't let you create an abstract class object except as the base class subobject of some derived class object. For example, these declarations won't compile:
shape s; // compile errorshape x[10]; // compile error
You can declare a pointer to a shape
, as in:
shape *ps; // OK
However, the compiler will reject a new-expression that tries to construct an abstract base class object, as in:
ps = new shape; // compiler error
Again, any class with at least one pure virtual function is an abstract class. If you can't create any such objects, then you don't have to worry that you might call a pure virtual function. Unfortunately, C++ compilers have trouble detecting all possible calls to pure virtual functions, such as when the constructor of an abstract class indirectly calls a pure virtual function of the same class. Thus, most compilers provide another line of defense. More on this shortly.
The definition for our 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;};
If you don't declare the area
and perimeter
functions in circle
, then circle
will inherit them as pure virtual functions, and circle
will be an abstract class. You won't be able to create any circle
objects. However, the class definition above does declare area
and perimeter
, so they're not pure virtual and circle
isn't an abstract class. Now you're on the hook to define circle
's area
and perimeter
functions.
Inasmuch as circle
is not an abstract class, you can create circle
objects, as in:
circle c (3);
or:
shape *ps = new circle (4);
If the compiler could detect and prevent all calls to pure virtual functions, implementing pure virtual functions would be trivial--simply initialize the vtbl entry for each pure virtual function as a null pointer. In C, the code to define the vtbl for the shape
base class would look something like:
typedef struct shape_vtbl shape_vtbl;struct shape_vtbl { double (*area)(shape const *); double (*perimeter)(shape const *);};shape_vtbl the_shape_vtbl = { 0, // 0 converted to null pointer 0 // ditto};
Unfortunately, compilers may fail to catch some pure virtual function calls. Such failures are rare, but nevertheless possible. As a second line of defense, if a pure virtual function has no definition, most compilers generate a definition for that function that makes a stink if executed. The C code to do this for the shape
class might look like:
double shape_area(shape const *s) { pure_virtual_alert(); return 0;}double shape_perimeter(shape const *s) { pure_virtual_alert(); return 0;}~~~shape_vtbl the_shape_vtbl = { shape_area, shape_perimeter};
On a desktop system, the pure_virtual_alert
function might simply write a message to the system's stderr
stream or display a message box, and then terminate the program. For an embedded system, you would have to tailor the response to be something more appropriate.
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 . .
Endnotes:
[1] Saks, Dan, "Virtual functions in C," Embedded.com, August 8, 2012. www.embedded.com/4391967.
[2] Saks, Dan, "Pure virtual functions," Embedded.com, December 12, 2012. www.embedded.com/4403313.
Thanks Dan! Awesome stuff. I'm new to C so I found all this info on pure virtual functions very interesting. Good reference here too:
http://www.programmerinterview.com/index.php/c-cplusplus/pure-virtual-function/