Search code examples
c++oopinterfacesolid-principles

"Has a" relationships in C++, is it best practice to have a class "implement" multiple abstract base classes?


In Java, you often use interfaces to indicate that a class will have a particular function.

In c++ I know this is done by abstract classes, however, I know there is often a stigma that goes a long with the multiple inheritance paradigm.

For example, Imagine you have game objects that will need to have functions on them: extends DealDamage will have a DoDamage() function extends TakeDamage will have a TakeDamage() function etc. How would you best handle this in C++?


Solution

  • If all you want is a contract-based ("interface, extends") design approach, just create classes that define the interfaces your implementation class is guaranteed to support.

    For example:

    class ITakeDamage {
        virtual ~ITakeDamage() {}     // virtual dtors for this design!
        virtual void TakeDamage(const DamageMeasurement& hurtFactor) = 0; // ouch!
        // ... some more stuff
    };
    
    class IDoDamage {
    public:
        virtual ~IDoDamage() {}
        virtual void HurtSomething(ITakeDamage* recipient, DamageMeasurement hurtFactor) = 0; // take that!
        // ... some more stuff
    };
    
    class NormalThingThatTakesAndGivesDamage : public ITakeDamage, public IDoDamage {
    public:
        NormalThingThatTakesAndGivesDamage();
        ~NormalThingThatTakesAndGivesDamage();
        // etc...
    
        // ITakeDamage interface members...
        virtual void TakeDamage(const DamageMeasurement& hurtFactor) override;
    
        // IDoDamage interface members...
        virtual void HurtSomething(ITakeDamage* recipient, DamageMeasurement hurtFactor) override; // implement this
    };
    
    
    class IndestructibleThingThatHurtsOthers : public IDoDamage {
    public:
        IndestructibleThingThatHurtsOthers();
        ~IndestructibleThingThatHurtsOthers();
        // etc...
    
        // IDoDamage interface members...
        virtual void HurtSomething(ITakeDamage* recipient, DamageMeasurement hurtFactor) override; // implement this
    };
    
    class HarmlessTarget : public ITakeDamage {
    public:
        HarmlessTarget();
        ~HarmlessTarget();
        // etc...
    
        // ITakeDamage interface members...
        virtual void TakeDamage(const DamageMeasurement& hurtFactor) override; // implement this
    };
    

    As Sam said in his comment, multiple inheritance is one of the more powerful features available in C++. Although I've used pure virtual interfaces here as an example (based on the "interface" portion of your question), inheriting from multiple base classes that also provide implementations is also appropriate in some cases. Used judiciously and with the right design, it can even considerably simplify relationships between classes. Used poorly, it can create a complete mess. Any "stigma" is generally from limited experience confined to bad or improper usage.

    Just because a tool isn't the right one for every situation, don't take it out of your toolbox. IMHO, if it makes your architecture simpler, more robust and easier to maintain, go for it.