Heavy use of virtual MI

I

Imre

Hi!

I've got some questions regarding heavy use of virtual inheritance.

First, let's see a theoretical situation, where I might feel tempted to
use a lot of virtual inheritance.

Let's suppose, we're creating a little strategy game. In our game,
there are Units. A Unit can be either a Human, or a Vehicle. Obviously,
Human and Vehicle are subclasses of Unit.

Now let's suppose that we'd like to use protocol classes, to separate
interface from implementation as much as possible. So we create a
UnitIntf class, and a UnitImpl derived from it. Likewise, we have
HumanIntf and HumanImpl. HumanIntf, besides inheriting from HumanIntf,
should also inherit from UnitImpl. Our class hierarchy now looks like
this (let's forget about Vehicles for now):

UnitIntf
/ \
UnitImpl HumanIntf
\ /
HumanImpl

Furthermore, let's suppose, that we want to seprate functionality
common to most strategy games from functionality specific to our
current game, so that we may easily reuse the common parts in our next
game (or in another one in parallel development). That is, we want to
create a GameEngine layer, and on top of that, a Game layer. So, in our
Engine, we have Unit and Human, interfaces and implementations. In
Game, we have to introduce some Game-specific stuff on the Unit level,
and then some other game-specific things on the Human level. So we will
need GameUnit and GameHuman classes. If we still stick to the interface
/ implementation separation, then we'll have the following classes and
inheritances:

EngineUnitIntf
EngineUnitImpl: EngineUnitIntf
EngineHumanIntf: EngineUnitIntf
EngineHumanImpl: EngineHumanIntf, EngineUnitImpl
GameUnitIntf: EngineUnitIntf
GameUnitImpl: GameUnitIntf, EngineUnitImpl
GameHumanIntf: GameUnitIntf, EngineHumanImpl
GameHumanImpl: GameHumanIntf, GameUnitImpl, EngineHumanImpl

I won't even try to draw this. (Actually, it would be easy in 3d: it's
a nice cube. On one axis, there's Engine / Game, on the other Unit /
Human, and the third Intf / Impl.)

Such design would make heavy use of virtual inheritance. In fact, _all_
the inheritances should be virtual (at least, if we suppose that Human
may have further subclasses).

Although the hierarchy may seem complicated at first, I think it can be
get used to, and once one's used to it, it can even be convenient to
use.

However, I'm not sure how much run-time overhead this would cause.

How is virtual inheritance typically implemented on current compilers?

Am I overusing inheritance here? What design alternatives are there to
create something similar? What's the performance comparison between
protocol classes vs. the pimpl idiom?

Thanks,

Imre
 
L

Luke Meyers

Imre said:
I've got some questions regarding heavy use of virtual inheritance.

First, let's see a theoretical situation, where I might feel tempted to
use a lot of virtual inheritance.

What you're describing is not virtual inheritance -- that's something
else entirely. Look it up. What you're describing is inheritance from
abstract classes -- i.e., those with only pure virtual functions
(analogous to Java interfaces).
UnitIntf
/ \
UnitImpl HumanIntf
\ /
HumanImpl

EngineUnitIntf
EngineUnitImpl: EngineUnitIntf
EngineHumanIntf: EngineUnitIntf
EngineHumanImpl: EngineHumanIntf, EngineUnitImpl
GameUnitIntf: EngineUnitIntf
GameUnitImpl: GameUnitIntf, EngineUnitImpl
GameHumanIntf: GameUnitIntf, EngineHumanImpl
GameHumanImpl: GameHumanIntf, GameUnitImpl, EngineHumanImpl

I won't even try to draw this. (Actually, it would be easy in 3d: it's
a nice cube. On one axis, there's Engine / Game, on the other Unit /
Human, and the third Intf / Impl.)

There are some obvious questions to be asked about such a design, on
the face of it. Regardless of your questions about efficiency, there
are OOD reasons not to have a million interfaces and a million levels
of hierarchy in a complex lattice. It's not just that it's difficult
to understand, though that can be part of it. It's inflexible. Many
small interfaces are good, but when you start having things like
parallel inheritance hierarchies of interfaces and implementations, it
really begins to not pay for itself. The poor chump at the bottom of
the hierarchy is tightly coupled to all of its parents and has way too
many responsibilities, as a result of having to support so many
interfaces. Just because something is a single sprite on a screen
doesn't mean that all its behavior need live within a single class.
For example, a Unit may have an x and y position, but the movement code
could be entirely separate, and just update x and y once it's figured
out where to move.
Such design would make heavy use of virtual inheritance. In fact, _all_
the inheritances should be virtual (at least, if we suppose that Human
may have further subclasses).

Subsituting again what you mean for what you said, I'll point out that
in C++, once a member function is virtual, it can never *stop* being
virtual farther down the inheritance chain. This isn't obvious,
because the virtual keyword is optional after the first use. Me, I'd
perhaps prefer if it were mandatory.
Although the hierarchy may seem complicated at first, I think it can be
get used to, and once one's used to it, it can even be convenient to
use.

Any design that you feel you must make excuses for and defend is
unlikely to be a good design. It's not just a question of how hard it
is to use, it's a question of how hard it is to change -- as with any
design.
However, I'm not sure how much run-time overhead this would cause.

How is virtual inheritance typically implemented on current compilers?

The rule of thumb is that every virtual function call costs about as
much as one extra pointer dereference at runtime, compared to a
non-virtual function call. If you want to know more about the
mechanism, STFW for terms like "virtual table," "virtual dispatch."
"dynamic binding," etc.
Am I overusing inheritance here? What design alternatives are there to
create something similar?

Quite possibly you are, yes. PreferCompositionToInheritance and obey
the SingleResponsibilityPrinciple. I made WikiWords out of those
because they're probably pages on c2, and you can go look there for
elaboration.
What's the performance comparison between
protocol classes vs. the pimpl idiom?

I assume by "protocol classes" you mean abstract interfaces. In which
case, this is apples-to-oranges. The two idioms do not solve similar
problems, and so comparing them is meaningless.

Luke
 
I

Imre

First, thanks for your answers. I've got some more questions though, if
you don't mind.

Luke said:
What you're describing is not virtual inheritance -- that's something
else entirely. Look it up. What you're describing is inheritance from
abstract classes -- i.e., those with only pure virtual functions
(analogous to Java interfaces).

Not neccessarily. Yes, some of the classes involved are abstract base
classes, but there are also some base classes that provide
implementation. For example, GameUnitImpl would be likely to inherit
some implementation from EngineUnitImpl. Also, Human inherits
implementation from Unit.
There are some obvious questions to be asked about such a design, on
the face of it. Regardless of your questions about efficiency, there
are OOD reasons not to have a million interfaces and a million levels
of hierarchy in a complex lattice. It's not just that it's difficult
to understand, though that can be part of it. It's inflexible. Many
small interfaces are good, but when you start having things like
parallel inheritance hierarchies of interfaces and implementations, it
really begins to not pay for itself. The poor chump at the bottom of
the hierarchy is tightly coupled to all of its parents and has way too
many responsibilities, as a result of having to support so many
interfaces. Just because something is a single sprite on a screen
doesn't mean that all its behavior need live within a single class.
For example, a Unit may have an x and y position, but the movement code
could be entirely separate, and just update x and y once it's figured
out where to move.

Yes, I agree with you, and here I'd use some kind of component based
design.

However, the original example was a bit different. I can see how to
factor out the movement code into a separate, contained object, but I
can't see how to do that when inheritance is used to separate the
abstract interface from the implementation (UnitIntf / UnitImpl), or to
separate different layers (EngineUnit / GameUnit).
Maybe the Unit / Human inheritance could be removed, if all units in
the game are instances of Unit, and they differ only in the components
they contain (eg. a vehicle has a WheelManager component added to it,
while a human doesn't), but... it feels a bit, well, unnatural to me.
PreferCompositionToInheritance and obey
the SingleResponsibilityPrinciple.

Generally I try to follow these rules (especially the one about single
responsibilities), but in the original example I'm not sure how I
should do it.

Thanks,

Imre
 
L

Luke Meyers

Imre said:
Not neccessarily. Yes, some of the classes involved are abstract base
classes, but there are also some base classes that provide
implementation. For example, GameUnitImpl would be likely to inherit
some implementation from EngineUnitImpl. Also, Human inherits
implementation from Unit.

Yes, of course -- the point was that "virtual inheritance" means
something else entirely, and you consistently used that term
incorrectly, so you should look it up and understand it to avoid that
mistake in the future.
However, the original example was a bit different. I can see how to
factor out the movement code into a separate, contained object, but I
can't see how to do that when inheritance is used to separate the
abstract interface from the implementation (UnitIntf / UnitImpl), or to
separate different layers (EngineUnit / GameUnit).
Maybe the Unit / Human inheritance could be removed, if all units in
the game are instances of Unit, and they differ only in the components
they contain (eg. a vehicle has a WheelManager component added to it,
while a human doesn't), but... it feels a bit, well, unnatural to me.


Generally I try to follow these rules (especially the one about single
responsibilities), but in the original example I'm not sure how I
should do it.

It's really impossible to say, since all you ever did was list the
interfaces, not saying anything about the responsibilities involved.
The movement example was my best guess at the sort of thing you might
mean. If you want to know whether you've grouped your responsibilities
appropriately (or rather, not grouped them, but not divided them
either), you'll have to say what they are.

Luke
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top