Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is multiple inheritance from the same base class via different parent classes really an issue here?

I want to implement an inheritance hierarchy in my C++ game engine that has some analogies from Java:

  • all objects inherit from Object class,
  • some functionality is provided by interfaces.

It's just an analogy that gives me some benefits (not a strict 1:1 Java:C++ mapping which is not possible).

By "interface" I mean here a class with only pure virtual, public methods. I know that it's not a strict and precise definition of it.

So I've made:

class Object{...};

/* interfaces */
class Nameable : public Object{...};
class Moveable : public Object{...};
class Moveable3D : public Moveable{...};
class Moveable2D : public Moveable{...};

class Model3D : public Moveable3D, public Nameable{...}
class Line3D : public Moveable3D{...}
class Level: public Nameable{...}
class PathSolver : public Object{...}

As you can see, Model3D now inherits from Object via multiple ways:

  • Moveable3D -> Moveable -> Object,
  • Nameable -> Object.

And my compiler in VS2013 (VC++) warns me about it.

Is that an issue?

If so, how to solve it to get a better inheritance structure?

I was thinking about two ways, but both have more disadvantages then advantages:

  1. Dropping the inheritance between e.g. Nameable and Object. But that way Level would lost its inheritance from Object too (and my first goal was: all objects inherit from Object class).
  2. I could also drop : public Object from each interface. But that way I would be force to manually type :public Object in each of many engine final classes (Model3D, Line3D, Level, PathSolver). That way both refactoring and creating new final types would be harder. Moreover the information that each interface will be guaranteed to inherit from Object is lost that way.

I would like to avoid virtual inheritance (one level of abstraction further) if it's not necessary. But maybe it is (at least for : public Object)?

like image 830
PolGraphic Avatar asked Dec 18 '14 11:12

PolGraphic


3 Answers

As said by Juraj Blaho the canonical way to solve this problem is virtual inheritance. One implementation model for virtual inheritance is to add an entry in the vtable of classes using virtual inheritance pointing to the real unique object from the base class. That way, you get the following inheritance graph :

             Model3D
            /       \
     Moveable3D   Nameable
           |         |
      Moveable       |
            \       /
             Object

That is you must have :

class Nameable : public virtual Object{...};
class Moveable : public virtual Object{...};

The other classes do not need virtual inheritance


Without virtual inheritance the graph would have been

             Model3D
            /       \
     Moveable3D   Nameable
           |         |
      Moveable       |
           |         |
        Object    Object

with 2 distinct instances of Object

The most tricky part in virtual inheritance is the call of constructors (ref Virtual Inheritance in C++, and solving the diamond problem) Because there is only a single instance of a virtual base class that is shared by multiple classes that inherit from it, the constructor for a virtual base class is not called by the class that inherits from it (which is how constructors are called, when each class has its own copy of its parent class) since that would mean the constructor would run multiple times. Instead, the constructor is called by the constructor of the concrete class. ... By the way, the constructors for virtual base classes are always called before the constructors for non-virtual base classes. This ensures that a class inheriting from a virtual base class can be sure the virtual base class is safe to use inside the inheriting class's constructor. The destructor order in a class hierarchy with a virtual base class follows the same rules as the rest of C++: the destructors run in the opposite order of the constructors. In other words, the virtual base class will be the last object destroyed, because it is the first object that is fully constructed.

But the real problem is that this Dreadful Diamond on Derivation is often considered as a bad hierarchy design and is explicitely forbidden in Java.

But IMHO, what C++ virtual inheritance does under the hood is not that far from what you would have if all your interface classes were pure abstract classes (only pure virtual public methods) not inheriting from Object and if all you implementation classes did inherit explicitely from Object. Using it or not is up to you : after all it is part of the language ...

like image 159
Serge Ballesta Avatar answered Oct 05 '22 14:10

Serge Ballesta


Use virtual inheritance of interfaces. That will ensure that there is only one instance of base class in the derived classes:

class Object{...};

/* interfaces */
class Nameable : public virtual Object{...};
class Moveable : public virtual Object{...};
class Moveable3D : public virtual Moveable{...};
class Moveable2D : public virtual Moveable{...};

class Model3D : public virtual Moveable3D, public virtual Nameable{...}
class Line3D : public virtual Moveable3D{...}
class Level: public virtual Nameable{...}
class PathSolver : public virtual Object{...}

Without virtual inheritance there are multiple instances of the same base in the object which will cause that you will not be able to substitute the concrete type where the interface is needed:

void workWithObject(Object &o);

Model3D model;
workWithObject(model);
like image 4
Juraj Blaho Avatar answered Oct 05 '22 16:10

Juraj Blaho


Yes, it is an issue: In the memory layout of Model3D, there are two subobjects of type Object; one is a subobject of Movable3D, the other of Nameable. As such, these two subobjects may contain different data. This is especially a problem, if the object class contains functionality that has to do with the identity of an object. For instance, an Object class that implements reference counting must be a unique subobject of all objects; an object that has two different reference counts is very likely a very bad idea...

To avoid this, use virtual inheritance. The only virtual inheritance that is needed is from the Object base class:

class Objec{...];

class Nameable : public virtual Object{...};
class Moveable : public virtual Object{...};
class Moveable3D : public Moveable{...};
class Moveable2D : public Moveable{...};

class Model3D : public Movable3D, public Nameable{...};
...
like image 3
cmaster - reinstate monica Avatar answered Oct 05 '22 14:10

cmaster - reinstate monica