简体   繁体   中英

C++ Multiple Inheritance Design Issue

I'm having doubts on how to proceed regarding the class structure of my game/simulation framework. Please note that I am doing this mostly for my own enjoyment, I know there are lots of good solutions already out there :)

The structure of the framework has, among other things, Scene s that contain Actor s. Both Scene s and Actor s can behave in many different ways WRT some aspects of how stuff works internally. I have implemented such behaviours using multiple inheritance, like so:

class ActorBase : public Object { ... }; 
class Actor : virtual public ActorBase { ... };
class CollisionBehavior : virtual public ActorBase { ... }; 
class BoundingBoxCollisionBehavior : public CollisionBehavior { ... };
class MyActor : public Actor, public BoundingBoxCollisionBehavior { ... };

This way, using virtual inheritance, I can separate the implementation of what the actor is from the implementation of what the actor does (or how it does it). Both the Actor and ...Behavior classes need access to the common base class ActorBase for their implementation, but things are neatly compartmentalised and I can easily change the behaviour of an object by simply replacing the Behavior class it inherits from.

This has its limitation though: since each behaviour follows its own internal rules, and an object can inherit from a single Behavior (or else hell breaks loose because of ambiguous function calls etc), I can never have two actors with different behaviours interact with each other. Also, since I'm integrating the V8 Javascript Engine into the framework, I'd have to create wrapped child classes for each possible combination of Actor (or Scene ) and their possible behaviours, and instancing these object in a mixed fashion from JS would result in a crash because, again, two object that behave differently cannot interact with each other (at least not for everything).

An alternative solution I thought would be to abandon this multiple inheritance structure in favour of something like this: each object that requires Behavior s has many arrays, one for each Behavior group (like collision behavior, movement behavior, etc), with a ...Behavior object for each possible behavior. I can give an object a default behavior and, if it's ever needed (an interaction occurs with an object with a different default behavior), I can add such behavior to one (or both) objects so that they can interact with each other - there would need to be some sort of conversion policy, but thats not an issue. This would mean having a circular reference (the object instance references the behavior instance and the behavior instance references the object it's contained in) for the behavior to be able to access the base properties it needs to do its own thing. The Behavior subclasses would also need to be friend s with the Actor class, to access protected and/or private members. I'm don't really like this road that much, and I think there might be a more elegant way to solve this problem, I just haven't thought of one yet.

Thoughts? :)

Bonus Question: should I spell Behavior or BehavioUr? Not a native speaker!

EDIT: Solved the bonus question - depends or where you feel you are when on the Internet! :)

EDIT 2: Changed from Behavior to CollisionBehavior for clarity (see answer below)

I am afraid you are mixing things here. As you noted there are many different kinds of behaviors 1 :

  • walking behavior
  • talking behavior
  • fighting behavior
  • ...

And of course it makes no sense to have a WalkingBehavior compare to a FightingBehavior so it makes no sense to have them share a base class either.

As such, I think that you should just use Actors and provide methods to retrieve the behaviors on those:

class Actor {
public:
    virtual WalkingBehavior const& walkingBehavior() const {
        return DefaultWalkingBehavior;
    }

    virtual FightingBehavior const& fightingBehavior() const {
        return DefaultFightingBehavior;
    }

    // ...
}; // class Actor

Where the default behavior is defined in such a way that it can interact with a more regular behavior of its class. Then, each specific interface such as FightingBehavior should provide the necessary methods to understand this behavior.

Note that this method of encoding information is extremely versatile: you can mix behaviors that are determined at compile-time and behaviors that are determined at run-time (and may evolve at run-time) without any incidence on the clients.

1 Personally I just follow my built-in browser spell-checker.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM