2

I found some code where derivation from a base class is used as a kind of flag to mark some objects as belonging to a specific group:

// Base type for any item to be stored in a Storage.
class Item {     // trivial class (as shown here)
public:
   virtual ~Item() {}
};

class BaseItem;   // complex class

class ItemA : public BaseItem, public Item;   // complex class

class Storage;

Since there already is a base class to derive from, we get multiple inheritance.

Is this a good way to proceed, considering the cost in terms of performance of multiple inheritance, and the fact that this code must be efficient?

Could a simple flag be a better option?

Pietro
  • 12,086
  • 26
  • 100
  • 193
  • 2
    The correct inheritance pattern depends entirely on the function of each component in relation to its effect on the final class. In general, one should prefer composition to inheritance, so the code already smells fishy. – Richard Hodges May 10 '16 at 10:38
  • 4
    Depends. What is the difference between `BaseItem` and `Item`? What are the similarities (e.g. does one derive from the other)? Why does `ItemA` need functionality or behaviour of both? If you can't answer those questions without hand-waving, then the need for multiple inheritance is questionable. – Peter May 10 '16 at 10:51
  • the naming itself is crooked. I'd expect Item to inherit from BaseItem, and ItemA to inherit only from Item, at least from looking at the naming. the naming is confusing – David Haim May 10 '16 at 10:55
  • @RichardHodges: if you need to collect different type of "items" runtime, composition -simply- cannot work. And if multiple dispatch is needed, dynamic_cast and RTTI are exactly the way to go. (don't answer "boost::any": it mask inheritance with type cancellation,but nerveless, always inheritance is!) – Emilio Garavaglia May 10 '16 at 11:10
  • 2
    There are simply not enough information about your type usage, to do whatever kind of reasoning. It will be better if you can provide some "motivation" for that design, or against it. – Emilio Garavaglia May 10 '16 at 11:13
  • Which item is the "flag", `Itrem` or `BaseItem`? If it is truly a flag as you say then there should not be any members or virtual functions (and hence no penalty associated). Perhaps the class actually adds functionality. – M.M May 10 '16 at 11:14
  • @M.M, it look like there is a virtual destructor, which all base classes __should__ have, but you are right that one virtual destructor doesn't really add any run-time overhead unless they are calling `delete` on a pointer to the base class. – Kyle A May 10 '16 at 11:17
  • @KyleA Virtual destructor is only needed for some purposes, but I guess he plans to use `dynamic_cast` to test if the 'tag' is present. Surely there are better solutions but as has been stated already, hard to say based on the info presented so far. – M.M May 10 '16 at 11:20
  • @M.M, I guess that would explain why he's talking about run-time overhead. I was assuming the usefulness of using a base class was to declare functions that took pointers to the base class. (Which would still require `dynamic_cast`ing to the derived class(es) to be useful, now that I think about it.) – Kyle A May 10 '16 at 11:23
  • 1
    @M.M, I will still stand by my statement that __all__ base classes need virtual destructors, because if someone tries to call `delete` on a pointer to the base class that really points to a derived object, the virtual destructor ensures that the right destructor gets called. Then again, I've learned to program defensively. I have to assume that someone is going to try to use my code incorrectly, and that assumption has almost always been right. – Kyle A May 10 '16 at 11:27

3 Answers3

3

The main question is: "How does the behavior of ItemA differ from that of BaseItem and from Item?" In fact, you could even ask: "How does the behavior of BaseItem differ from Item?" Inheritance is principally used when you want to inherit common behavior among types which nevertheless have some differences in behavior among them (cf. "Why use inheritance at all?"). If these items don't differ in behavior at all, there are much cleaner ways of denoting some special "type" of each item without having to define an actual... type for them, e.g. with a flag as you already proposed. When in doubt, keep it simple.

In terms of performance... Well, don't even think about performance at all at this stage: Yes, there will be a very small runtime impact of using virtual members, but for nearly all purposes this will be irrelevant. However, with multiple inheritance it will be easy to quickly make a brain-pain-inducing mess which no one wants to try to understand. Still, there are some good reasons for using MI, but be very, very careful with it.

Community
  • 1
  • 1
errantlinguist
  • 3,658
  • 4
  • 18
  • 41
2

Sure that's perfectly fine. It's how C++ does it. For example, basic_istream and basic_ostream both inherit from a base class basic_ios. Then basic_iostream inherits from both basic_istream and basic_ostream. Looks like this:

https://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-3.4/classstd_1_1basic__iostream__inherit__graph.png

2

A simple flag could be a better option, but it depends. I've seen this type of trivial base class used to allow compile-time checking that an object is allowed to do something.

Whether it runs faster to do this or to use a flag depends on how you are using it. In the example I gave, the code was then using dynamic_casts to get the derived pointer back from the base pointer. That has a run-time cost, which may have made the flag more efficient, but the programmer wanted that compile-time check to prevent other programmers from trying to use the system incorrectly, so he made a trade-off.

Virtual tables do have a performance overhead, but not much of one (tests that I have performed lead me to believe the performance is just barely slower than that of a switch statement), so I wouldn't worry about that unless you are programming an embedded system or writing a function that will be performed millions of times per second or more.

Kyle A
  • 928
  • 7
  • 17