4

I would like to define interfaces for different properties of my classes that can be accessed from external methods/classes.

  • I use multiple implementation inheritance (= inheritance from concrete classes which opposed to interface inheritance) which is considered a bad practice (point 3 from here).

  • I use inheritance for code reuse. As said here, it is better to use composition over inheritance for code reuse. But with composition, all my MyObj1,MyObj2,...,MyObj1000 will have the same get and set methods, while I have to reimplement them really rarely.

Is this scenario is perfect illustration of situation when multiple implementation inheritance for code reuse is good or this code completely wrong and I should use some clever syntax / patterns to avoid such design? I ask this question because I believe in latter.

#include <string>
#include <iostream>

// interface of object with name
class BaseName {
public:
    virtual void setName(const std::string& val) { name = val; }
    virtual std::string getName() const { return name; }
private:
    std::string name;
};

// user of interface of objects with name
void nameUser(BaseName* b) {
    std::cout << b->getName() << std::endl;
}

// interface of object with id
class BaseID {
public:
    virtual void setID(int val) { id = val; }
    virtual int getID() const { return id; }
private:
    int id = 0;
};

// user of interface of objects with id
void idUser(BaseID* b) {
    std::cout << b->getID() << std::endl;
}

class MyObj1 : public BaseID, public BaseName {
public:
    void setName(const std::string& val) override { 
        /* update internal state that depends on name. this is why "virtual" is required */
        BaseName::setName(val);
    }

    /* methods and fields specific to MyObj1 */
};

// MyObj2,...,MyObj999

class MyObj1000 : public BaseID, public BaseName {
public:
    /* methods and fields specific to MyObj1000 */
};

int main() {
    MyObj1 o1;
    o1.setName("xxx");
    o1.setID(18);

    MyObj1000 o2;
    o2.setName("yyy");
    o2.setID(-738);

    nameUser(&o1);
    nameUser(&o2);
    idUser(&o1);
    idUser(&o2);
}
  • 1
    what is "multiple implementation inheritance" and why is it considered bad practise? can you provide a reference? I have to admit I never heard about that before and searching for it I only found one site that didnt look very trustworthy – 463035818_is_not_an_ai Oct 29 '18 at 15:05
  • Pure interfaces vs base classes that have an implementations. I don't see a problem with base classes that have implementations. But it's primarily an opinion problem. – Matthieu Brucher Oct 29 '18 at 15:06
  • 1
    Do you mean _"multiple inheritance"_ instead of _"multiple implementation inheritance"_? – Jabberwocky Oct 29 '18 at 15:11
  • You might want to have a look at mixin inheritance. – Nico Schertler Oct 29 '18 at 15:14
  • By implementation inheritance I mean inheritance from class with data members, not a pure interface. –  Oct 29 '18 at 15:23
  • " I mean inheritance from class with data members, not a pure interface" and where did you read that this is considered bad practise? ( i am not disagreeing, just a bit surprised that you take it for granted that inhertitance is bad) – 463035818_is_not_an_ai Oct 29 '18 at 15:25
  • @user463035818 I think it's just about the diamond problem. – Max Langhof Oct 29 '18 at 15:30
  • @user463035818 I have added link in question. –  Oct 29 '18 at 15:52
  • well, ok the answer you linked is about multiple inheritance (no "multiple implementation inheritance", i see you also changed the question accordingly). I think the problem with inheritance is just that (at least it was the case for me) it is thaught as if it is the most important feature of OO, after taking my first OO lecture i was convinced that if you dont inherit everything from anything then you are not really doing OO. The reality is that inheritance, just like anything else, can be overused and if you make it your default solution for everything then you are doing it wrong. – 463035818_is_not_an_ai Oct 29 '18 at 16:04
  • 1
    Multiple inheritance is not a problem if done right. Your code, however, has multiple problems: 1. If you provide the vanilla getter *and* setter for a member, you might just as well make that member public. 2. Your bases are nothing more than fancy names for each member your classes may have. That's just not right, classes are there to *abstract* from things. 3. You declare your getters/setters `virtual` without any reason, whatsoever. --- Bottom line: Try to model behavior, don't get lost creating complex indirections to access the raw data. Those indirections earn you nothing. – cmaster - reinstate monica Oct 29 '18 at 16:30
  • Many C++ programmers are using multiple inheritance, complete with the [diamond of dread](https://en.cppreference.com/w/cpp/io/basic_iostream), on a regular basis. Some of those same programmers are then telling us that multiple inheritance should be avoided at all costs. Go figure. – n. m. could be an AI Oct 29 '18 at 17:02

2 Answers2

1

You should only inherit if your derived class IS-A base class, that is it makes sense for it to act in your system as the class it inherits from. Where multiple inheritance comes in is where your derived class exists in multiple "domains", for instance you might have a bird class that is also serializable, you would inherit from both animal and serializable, and it would be OK to inherit implementation from each.

But the fact that you have 1000 derived classes is a Code Smell to me. You perhaps should step back and take a wider look at your design and what you're trying to achieve. Knowing more about that would help us give better answers.

Rob K
  • 8,757
  • 2
  • 32
  • 36
  • I think in this case the "IS-A" would be solved by a slight name change to `HasId`, `HasName` etc. for the base classes. – Max Langhof Oct 29 '18 at 15:24
  • @MaxLanghof isnt this cheating? You can always rename a base-class from `Foo` to `hasFoo`, but that wont really improve the design ;) – 463035818_is_not_an_ai Oct 29 '18 at 15:32
  • @MaxLanghof No, most definitely not. Calling a skunk a rose doesn't make it so. If you inherit, no matter why, you are declaring an IS-A relationship and should try to adhere to the [Liskov Substitution Principle](https://stackoverflow.com/questions/56860/what-is-an-example-of-the-liskov-substitution-principle). – Rob K Oct 29 '18 at 15:47
  • The is-a rule only applies if talking about public inheritance, there are (rare) times where using private inheritance makes in-terms-of implementation much easier. – SoronelHaetir Oct 29 '18 at 16:01
  • @SoronelHaetir I don't necessarily disagree with that, but I don't think that's where one should start with a design, especially for a less experienced developer. I think that we experienced devs can get too hung up on justifying the use of a feature in edge cases and not focus enough on the 90% of the time that feature isn't the right solution. – Rob K Oct 29 '18 at 17:10
1

You should only inherit if your derived class IS-A base class, that is it makes sense for it to act in your system as the class it inherits from.

That said multiple inheritance is perfectly fine in C++. A class can have multiple IS-A base classes. It's also good practice with so called mix-in classes or interfaces. Classes that add a bit of functionality to the class without bothering the rest of the class much. For example inheriting a Serializable base class.

One thing I would try to avoid though is the diamond problem. That is inherit two or more classes that in turn inherit from the same base class. For example

class Base { int base; };
class A : public Base { };
class B : public Base { };
class X : public class A, B { };

In such situation it is hard to understand when X.base comes from and easy for A and B to make different use of the Base class. That will lead to hard to debug errors.

Goswin von Brederlow
  • 11,875
  • 2
  • 24
  • 42
  • Sometimes, you don't want to avoid the diamond, but rather declare the inheritance from the common base `virtual`. That way, your objects contain only a single subobject of type `Base`, avoiding any ambiguity. – cmaster - reinstate monica Oct 29 '18 at 16:19
  • 99% of the diamond inheritance example problems I've seen are from implementing a HAS-A relationship with inheritance instead of composition. I think in 20+ years of professional development, I've actually encountered any real world instance of it once. – Rob K Oct 29 '18 at 17:13