Archived

This topic is now archived and is closed to further replies.

Kars

Multiple Inheritance. Good or bad

Recommended Posts

specifically in C++. I was planning on doing something like having three classes and depending on what I wanted, inheritance from them. i.e. Base classes 1) VObject - draw an object. 2) SObject - used to make an object "sold" (collision detection) 3) Movement - used to handle moving objects. So if I wanted an invisible wall that moved around the board I would inheritance from SObject and Movement. If I wanted a bird flying in the sky that was just eye candy (can''t be hit, etc) inheritance from VObject and Movement. etc.... This seem like a good Idea? or is there a better way to handle this? Any performance penaltys attached to Multiple Inheritance? Harder to debug? Do most compilers handle it the same way?

Share this post


Link to post
Share on other sites
Rember what public inheritance is. It exprepresses an "is-a" relation. "Is" a bird a movement? Obviously not. It''s a bit harder with VObject and SObject. Well, you could say: a bird *is* a visual object (that''s what I intend the V at VObject to be ment for) and it *is* a solid object. But what you really try to add to your object is a special behavior. It is "drawable" "collidable" "movable". That''s why you would better stick to interfaces (like jonnii and daerid told you already) as they express a supported behavior. Only use inheritance if you want to *specialize* a given class. Example: class Woman inherits from clas HumanBeing. A woman (in deed) *is* a HumanBeing, but with specialized properties (which we all know soooo well - sorry girls *g*).

Hope that helps to explain why MI is not a proper usage in this case.

In fact, MI is (almost) never neccessary. Guess why Java, .NET etc. got rid of it?

Regards,
VizOne

Share this post


Link to post
Share on other sites
quote:
Original post by VizOne
Guess why Java, .NET etc. got rid of it?

Mostly because it is really hard(and costly) to implement correctly.
Incidentally, Eiffel.NET DOES support MI. It had to, since the standard Eiffel libraries rely so much on MI(Any Eiffelite will tell you that MI in C++ is totally braindamaged, while Eiffel got it right).

--
AnkhSVN - A Visual Studio .NET Addin for the Subversion version control system.
[Project site] [Blog] [RSS] [Browse the source] [IRC channel]

Share this post


Link to post
Share on other sites
Ah, more thoughtless MI-bashing.

quote:
Original post by VizOne
Rember what public inheritance is. It exprepresses an "is-a" relation. "Is" a bird a movement? Obviously not.


That''s just semantics, really. Suppose Movement was called MovableObject: a bird "is" a MovableObject.

The point of inheritence, multiple or otherwise, is firstly to arrange a heirarchy of kinds -- a bird is a kind of solid object, a kind of visual object, and a kind of movable object -- and secondly to allow classes to reuse code from other classes.

Here, it is likely that the solid object class contains much that is relevant to collision detection. This includes methods for checking for collision, and therefore information about the shape of the bird.

The visual object class contains members relevant to rendering objects. This includes methods for actually rendering the object, and therefore information about the appearance of the bird.

The movable object class contains members relevant to object movement. Methods for applying physics to the object is likely to be here. There is some overlap between solid objects and movable objects, since physics may go as far as collision response between the bird and the rest of the world. This may imply that the movable object class is bogus.

Clearly, a bird will need to use the features of all of these classes. With inheritance from virtual classes for Drawable, Movable and Collidable, all the code for drawing, moving and colliding the bird must be replicated in the bird. This runs contrary to the principle of code reuse. You end you having to write the same code repeatedly, which suggests this particular design is bogus.

C++ still has issues, though. Observe that the visual and solid object classes both need information about the geometry and appearance of the bird. In C++, each class holds that information seperately, and therefore memory is wasted. As Arild points out, Effiel is better -- in this particular situation you could set things up quite easily so that only one copy of the bird''s model is kept.

Share this post


Link to post
Share on other sites
To throw my two cents in, I recently worked on a project with a considerably deep class heirarchy for all objects in the game, done to provide this kind of system. The class heirarchy, starting from the top at ''Player'' went like this:

Player->Human->Biped->PhysicsBiped->DrawableBiped->DrawableObject->Object.

Unfortunately, at some point we wanted Human objects that had no physics within them - they were animating NPCs without any need for collision detection.

Because of our heirarchy it was impossible to remove the physics code, and more importantly the variables & state information, from the Human. The sollution involved putting the state information for PhysicsBipeds into a structure that could be dynamically allocated, effectively allowing us to remove the PhysicsBiped from the heirarchy. This is a horrible sollution, but worked for our time frame.

In any further development I would use an aggregate container class that either held references to classes that provided the required tasks or registered itself with some kind of feature manager that let the required functionality and state variables be assigned to the object without the cumbersome class heirarchy.

What does this mean to you? Well I would be worried about implementing multiple inheritance since you pull in a lot of functionality that may at first seem reasonable, but may ultimately be unused as the design of your project changes. I don''t have an elegant sollution for you at this time; I''m still experimenting with it myself. Granted our problem with compounded by being a title targetted at multiple platforms simultaneously.

- S

Share this post


Link to post
Share on other sites
Personally, I don''t have much issue with multiple inheritance, I''m just really hard pressed to find situations that really merit it.

The following is my take on the matter and the greater issue.

This falls under to the language features debate. Which in and of itself needs to be debated and understood.

Language is what we use to express ourselves across a specific medium -- realise that a change in medium has a dramatic effect on the language or at least the message.

There are two extremes. One is an exceptionally static language, that doesn''t change and you use it to SOLELY build more vocabulary but no syntactic constructs. The other is that the language is very simple and to a significant degree redefinable such that one can not only create new vocabulary but one can also build new syntatic constructs. Now depending on the language''s and the programmer''s philosophy, they''ll take varied approachs to solving a problem.

If we take Java as an example, the language is very static, "good" programming practices are enforced everywhere. The idea of multiple inheritance is taken away and interfaces are brought in if one is looking for multiple-inheritance -- you''ll just find it in a diluted form.

You simply have to decide where you wish to fall within the aforementioned spectrum. Once you know that, you''ll have an idea what the truth is and will act accordingly. If you have the facilities to fall on one side of the debate and figure out what is "good" then you''ll know if multiple inheritance is "good", just remember, that it maybe easy for you to deal with multiple inheritance but will it be for those maintaining your code -- this of course could be you.

Share this post


Link to post
Share on other sites
Good, but much like a chainsaw or blowtorch, shouldn''t be placed in unexperienced hands. It''s very easy to maim your program with poor multiple inheritance use, but not having the feature when you need it would suck.

In your case, don''t use MI. Why?

VObject, SObject, Movement, Health, Armor, who knows how far you may want to extend an object''s capabilities. The result is:


class CKnight : public VObject, SObject, Movement, Health, Armor


Not only is that ugly and slow to compile, but good luck not accidentally colliding member names and trying to keep track of what''s going on. Do it the interface way like others said.


CustomBar

Share this post


Link to post
Share on other sites
quote:
Sphet
Player->Human->Biped->PhysicsBiped->DrawableBiped->DrawableObject->Object



This also means that players could only be humans. Surely an obvious solution would have been to add a Boolean toggle to PhysicsBiped that indicates whether or not physics should be applied?

quote:
Original post by LordElectro
Good, but much like a chainsaw or blowtorch, shouldn't be placed in unexperienced hands. It's very easy to maim your program with poor multiple inheritance use, but not having the feature when you need it would suck.

In your case, don't use MI. Why?

VObject, SObject, Movement, Health, Armor, who knows how far you may want to extend an object's capabilities. The result is:


class CKnight : public VObject, SObject, Movement, Health, Armor


Not only is that ugly and slow to compile, but good luck not accidentally colliding member names and trying to keep track of what's going on. Do it the interface way like others said.


Seeing as the above line would be the same with interfaces, and that member names of interfaces can collide, I don't see how interfaces are going to solve these problems.

Sure, interfaces make it easier to see what's going on: because you write all the code yourself. You can apply the same rule to single inheritence and conclude that you should never inherit implementation, no matter what, because you might not know what's going on in the class.

If the classes you inherit from are well designed and documented, you will always know what a particular superclass is going to do in a particular situation.

EDIT: Darned quote tags.

[edited by - Mayrel on October 22, 2003 6:49:38 AM]

Share this post


Link to post
Share on other sites
quote:

.e.
Base classes
1) VObject - draw an object.
2) SObject - used to make an object "sold" (collision detection)
3) Movement - used to handle moving objects.

So if I wanted an invisible wall that moved around the board I would inheritance from SObject and Movement.
If I wanted a bird flying in the sky that was just eye candy (can''t be hit, etc) inheritance from VObject and Movement.
etc....



How will client code use these classes/interfaces? Can you show a code snippet or even just psuedocode?

--
Dave Mikesell Software & Consulting

Share this post


Link to post
Share on other sites
I'm don't knwo how your game engine is layed out, but a kernel approch seems popular. You have a game engine that houses services for your game objects to use.

When you create the object, you register it with the engine. When it's registered, it tells the engine what services it requiers. It hooks-up each interface it implements to the corresponding service the kernel provides.

An example service, is a virtual-time-phase-locked timer that invokes a virtual Tick(Time now, Kernel*) method on your object when it is time to update the object for the next simulation round. In this update routine, you can invoke whatever kernel services are required by your object - call into the physic engine to do work to update your velocity vectors etc... call into the graphics engine to manipulate your textures, meshes, etc... call into the audio engine to set up a sfx.

After calling Tick on all the objects, then you update the actual audio (mix in new sounds, stop expired soudns) & graphics (render the next frame).

Another example service, could be a collision detector. You tell the CD your object cares about collisions, and if it detects a collision, it calls your object - say through the ICollidiable::HitDetected(Kernel*, ICollidiable* object_hit) interface - to inform the object of the event. You could have a couple of different collide events this way, maybe one for CD with an immobible objects (walls), a different one for two moving objects hitting each other, and maybe a special one for weapon hits - ICollidiable::ImHit(Kernel*, IWeapon*).
(You would also want to use the CD for rendering - CD the viewing frustrum with the geometry, and you have the objects that need to be drawn).



quote:
Original post by VizOne
In fact, MI is (almost) never neccessary. Guess why Java, .NET etc. got rid of it?


They didn't get rid of it, they just restricted it to abstract classes/interfaces only; more like rebranding than removal.

[edited by - Magmai Kai Holmlor on October 22, 2003 12:51:04 PM]

Share this post


Link to post
Share on other sites
I personally use multiple inheritance quite a lot in almost every project, however, it is rarely ever (almost never, but still occassionally) public inheritance. For the most part, the types have 1 or no public base classes and the rest are only private bases, so those types don't follow an "is-a" relationship. I guess this is more of a reply in support of private inheritance rather than multiple inheritance in general.

The main thing I use this for is when i have to encapsulate an object (and it only makes sense for it to encapsulate one) but you need to expose some of that object's interface. If you use private inheritance, however, you can use the using syntax to expose that object's interface publically.

for instance:

class Something
: public SomeBase,
private Offsetxy< float >,
private Noncopyable
{
private:
typedef Offsetxy< float > OffsetType;
public:
using OffsetType::VectorType;
using OffsetType::ComponentType;
using OffsetType::ScalarType;
public:
/* blah blah blah */
using OffsetType::SetOffsetx;
using OffsetType::SetOffsety;
using OffsetType::SetOffset;
using OffsetType::GetOffsetx;
using OffsetType::GetOffsety;
using OffsetType::GetOffset;
private:
/* blah blah blah */
};


This allows you to give anything an offset without ever having to remake functions to access encapsulated objects. Just privately inherit from it and promote their member functions access from private to public.

The second private base, Noncopyable, is like boost's. I was just using it to show that it is very conceivable to have quite a few private bases for one type.

Despite what everyone claims, multiple inheritance is not some horrible horrible thing. Here, it prevents you from having to write redundant code (which also can reduce bugs), and it even makes your code more clear and readable. Just make sure that if you start using multiple PUBLIC bases that you are absolutely that is what you want. More often than not, beginner/intermediate (and even sometimes fairly good) programmers think that multiple public inheritance is what they want, when in actuality they really should use aggregation and/or private bases. While this is not always the case, just question whether or not multiple public bases is actually what you need.

[edited by - Polymorphic OOP on October 22, 2003 4:30:25 PM]

Share this post


Link to post
Share on other sites
The diamond problem can be solved (virtual inheritence), but yeah, you''d want to have a close look at your class structure if this happens.

Share this post


Link to post
Share on other sites
quote:
Original post by Chris Hare
MI is allright when not abused, e.g. diamond problem.


Mmh... even then, there are some cases where the diamond pattern is the right way to do things. I''ve got an interface class and an implementation class - if I want to extend those to add new functions to the interface, I have to derive first from the interface class (to get a new interface), and then from the new interface and the old implementation. The result is a diamond pattern.

Richard "Superpig" Fine
- saving pigs from untimely fates, and when he''s not doing that, runs The Binary Refinery.
Enginuity1 | Enginuity2 | Enginuity3 | Enginuity4
ry. .ibu cy. .y''ybu. .abu ry. dy. "sy. .ubu py. .ebu ry. py. .ibu gy." fy. .ibu ny. .ebu
OpenGL is a language

Share this post


Link to post
Share on other sites