Destructors for Virtual Base Classes

Destructors for virtual base classes are called in the reverse order of their appearance in a directed acyclic graph (depth-first, left-to-right, postorder traversal). Figure 11.1 depicts an inheritance graph.

Figure 11.1   Inheritance Graph Showing Virtual Base Classes

The following lists the class heads for the classes shown in Figure 11.1.

class A
class B
class C : virtual public A, virtual public B
class D : virtual public A, virtual public B
class E : public C, public D, virtual public B

To determine the order of destruction of the virtual base classes of an object of type E, the compiler builds a list by applying the following algorithm:

  1. Traverse the graph left, starting at the deepest point in the graph (in this case, E).

  2. Perform leftward traversals until all nodes have been visited. Note the name of the current node.

  3. Revisit the previous node (down and to the right) to find out whether the node being remembered is a virtual base class.

  4. If the remembered node is a virtual base class, scan the list to see whether it has already been entered. If it is not a virtual base class, ignore it.

  5. If the remembered node is not yet in the list, add it to the bottom of the list.

  6. Traverse the graph up and along the next path to the right.

  7. Go to step 2.

  8. When the last upward path is exhausted, note the name of the current node.

  9. Go to step 3.

  10. Continue this process until the bottom node is again the current node.

Therefore, for class E, the order of destruction is:

  1. The nonvirtual base class E.

  2. The nonvirtual base class D.

  3. The nonvirtual base class C.

  4. The virtual base class B.

  5. The virtual base class A.

This process produces an ordered list of unique entries. No class name appears twice. Once the list is constructed, it is walked in reverse order, and the destructor for each of the classes in the list from the last to the first is called.

The order of construction or destruction is primarily important when constructors or destructors in one class rely on the other component being created first or persisting longer — for example, if the destructor for A (in the graph in Figure 11.1) relied on B still being present when its code executed, or vice versa.

Such interdependencies between classes in an inheritance graph are inherently dangerous because classes derived later can alter which is the leftmost path, thereby changing the order of construction and destruction.