C++ tips #1

February 9, 2011

Virtual Class Inheritance in C++

Java has notions of both class inheritance and interface inheritance, where interfaces are only sets of methods. To mimic this very useful behavior in C++ I often define classes with only a single method, such as

class Point;

struct PointSet
{
  virtual bool contains (const Point & const) const = 0;
};

class Point
{
  ...
public:
  inline bool in (const PointSet & point_set) const
  {
    return point_set.contains(*this);
  }
  ...
};

so that when defining objects which “are”, among other things, sets of points, they can inherit as so:

class Environment
  : public PointSet
{
  ...
public:
  virtual bool contains (const Point & point) const { ... }
  ...
};

In complicated class hierarchies, it can be useful to aggregate such abstract interface classes, e.g.,

struct MetricSpace
  : public PointSet
{ ... };

struct VectorSpace
  : public PointSet
{ ... };

struct BanachSpace
  : public MetricSpace,
    public VectorSpace
{ ... };

But oops, BanachSpace is now a PointSet in two distinct ways, i.e. via VectorSpace::contains and via MetricSpace::contains. To avoid this so called “diamond problem”

          PointSet                  PointSet    PointSet
         /        \                     |           |
  VectorSpace  MetricSpace   vs   VectorSpace   MetricSpace
         \        /                      \         /
         BanachSpace                     BanachSpace

C++ designers invented virtual inheritance which allows a unique inheritance of each interface-like method, so that in

struct MetricSpace
  : public virtual PointSet
{ ... };

struct VectorSpace
  : public virutal PointSet
{ ... };

struct BanachSpace
  : public virtual MetricSpace,
    public virtual VectorSpace
{ ... };

References

C++ FAQ Lite,
Wikipedia

BanachSpace::contains is unique.

C++0x

Gcc 4.4 and later support nice C++0x features like hash tables via std::unordered_map and auto typed variables, as in

std::unordered_map<
  std::pair,
  std::vector<std::pair > > widgets = get_widgets();

for (auto i = widgets.begin(); i != widgets.end(); ++i) {
  std::swap(i->second.first, i->second.second);
}

The new standard also allows variadic initializer lists with a small speed penalty.

My favorite features are:

3 Responses to “C++ tips #1”

  1. karlobermeyer Says:

    Is the only purpose of virtual inheritance to solve the diamond problem?

    If so, then it seems like the C++ language could have been made so that the compiler automatically checks for ambiguity when calling inherited functions. Apparently in Python all functions are virtual. This makes me wonder whether there is some limitation or speed reason for not having it automatic in C++…

    • fhobermeyer Says:

      I think we have confused virtual inheritance of classes with virtual inheritance of methods.

      Yes there is a speed penalty to virtually inheriting methods in C++: it requires run-time method lookup in the vtable, and hence additionally prevents inlining.

      No the purpose of virtual methods is not related to the diamond problem. The purpose of virtual methods is to specify an abstract interface (a set of methods) in a base class, and allow derived classes to implement these methods. This allows extension of a class hierarchy after compile-time.

      Virtual inheritance of classes does address the diamond problem.

      • karlobermeyer Says:

        I’m sorry the second paragraph of my response was confusing. I do understand that virtual inheritance of classes is different than a virtual function, but I do not understand (1) whether the only purpose of virtual inheritance is to solve the diamond problem and (2) why is not all inheritance virtual?


Leave a comment