1) The interface is good, and you can add a default implementation this way:
class IBiting { public virtual void bite() = 0 }; class HasTeeth, public IBiting { Teeth teeth; public: virtual void bite() override { teeth.bite(); } }; for(Animal* a: animals) { IBiting* it = dynamic_cast<IBiting*>(a); if(it) it->bite(); }
1b) ... you can completely remove the interface and use only HasTeeth :
class HasTeeth { Teeth teeth; public: void bite() { teeth.bite(); } }; for(Animal* a: animals) { HasTeeth* it = dynamic_cast<HasTeeth*>(a); if(it) it->bite(); }
2) Animal explosion can be used if you do not want to use RTTI / dynamic_cast . . You can make virtual void bite() with an empty implementation on Animal and override it later (adding Teeth once). Not much code if you insist on not using RTTI, but if you can use dynamic_cast , why not use it?
EDIT: The answer from Vaughn Cato is perfect for this - virtual / abstract teethPtr() (or getTeeth() ) in Animal with short abbreviations like biteIfYouCan() . Good for the embedded world (microchips), but for PC, I still prefer dynamic_cast .
3) Virtual inheritance can help us with BitingAnimal vs. FlyingAnimal :
class BitingAnimal: public virtual Animal { Teeth teeth; public void bite() { teeth.bite(); } }; class FlyingAnimal: public virtual Animal { Wings wings; public void fly() { wings.fly(); } }; class FlyingBitingAnimal: public FlyingAnimal, public BitingAnimal {};
4) The combination of Animal and Teeth does not make sense if you do not completely remove Teeth and replace it with HasTeeth or CanBite . Then he becomes my 1b.
5) This listing is another version 2 - a bloated animal. Not good. But this leads me to an alternative that does not use dynamic_cast: you can simulate it using features (this enumeration or flags on animals - bool can_bite ) that can tell you that the cast is safe. Then you can use multiple / virtual inheritance to simulate dynamic_cast (first check the validation capabilities, then click below).
EDIT: Vaughn Cato teethPtr() also matches this ( show me teeth that can be bitten if you have them ) and does not need to be broadcast.
Reply to comment:
In short: try to name a function (ability, ability to do something to provide something).
Long answer: Do you need Teeth and HasTeeth or single CanBite ? Your fourth decision is not bad in principle, but in the name and, therefore, in other possibilities. All of this was hypothetical. Interfaces are well known in other languages ββ(single inheritance + interfaces), HasTeeth is something like C # IListSource with IList GetList() and bool ContainsList (for erasing), where bite() does not exist directly, but can be added using the extension method :
static IEnumerator GetEnumerator(this IListSource it) { if(!it.ContainsList) yield break; foreach(object o in it.GetList()) yield return o; }
Here you can see that I achieved the same goal with the C # extension method as with C ++ multiple inheritance. The name is-a is SourceOf . Can you share real names with us from your GUI?
An example from C ++ would be iostream = istream + ostream with virtual inheritance from ios. Again is-a .