C/C++

Storage Layout of Polymorphic Objects

By Dan Saks, November 15, 2012

Adding at least one virtual function to a class alters the storage layout for all objects of that class type.

Adding at least one virtual function to a class alters the storage layout for all objects of that class type. In this article, I begin to explain how C++ compilers typically implement virtual functions by explaining how the use of virtual functions affects the storage layout for objects.

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." All the shapes also have common attributes, such as position, and outline, and fill colors. Each derived class adds some linear or angular distance(s) to characterize the physical extent of the shape. For example, a circle has a radius, a rectangle has a height and a width, and a triangle has two sides and an angle.

(As at least one reader observed earlier, the shape class should probably be an abstract base class with pure virtual functions. That's not relevant to this discussion. Ditto for virtual destructors.)

If the area and perimeter member functions weren't virtual, then the functions wouldn't occupy any storage within shape objects. The storage for a shape object would contain only the storage for the data members, as shown in Figure 1.

Figure 1: The storage layout of a shape object if classs shape had no virtual functions.

However, shape's area and perimeter member functions are virtual. A class type, such as shape, with a least one virtual function is a polymorphic type. An object of polymorphic type is a polymorphic object. C++ compilers typically add a pointer to the storage layout of each polymorphic object. That pointer is commonly called a vptr ("VEE-pointer") and it points to a table of function pointers called a vtbl ("VEE-table").

Each polymorphic type has its own vtbl. That vtbl contains one pointer for each virtual function in the class. For example, the shape class has two virtual functions, area and perimeter, so the vtbl for shape contains one pointer to the shapearea function and another to the perimeter function, as shown in Figure 2.

Figure 2: The storage layout for shape objects and shape's vtbl.

Figure 2 shows the vptr situated at the beginning of each shape. Some compilers place the vptr after the last data member, instead. Each compiler can do as it pleases, as long as every object of a given polymorphic type has the same storage layout.

Each derived class inherits all the data members of its base class. Inherited members must have the same offsets within the derived class as they do in the base class. (This is true for classes with only one base class. It may not be true for classes with multiple base classes  a complication that I'm going to ignore for now.)

A class derived from a polymorphic base class will be polymorphic as well, and it inherits the base class's vptr. The vptr must have the same offset in the base class subobject (the base class portion) of a derived class object as it does in a base class object.

The circle class is polymorphic and it defines its own versions of area and perimeter. The compiler generates a distinct vtbl for class circle. Except that each circle object has the additional data member radius, the storage layout for circle objects, shown in Figure 3, is identical to the layout for shape objects.

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.

This month's Dr. Dobb's Journal

This month,
Dr. Dobb's Journal is devoted to mobile programming. We introduce you to Apple's new Swift programming language, discuss the perils of being the third-most-popular mobile platform, revisit SQLite on Android
, and much more!