4

We have a child class practicante who inherits from 2 classes: estudiante and empleado and both inherits from a grandfather-class persona. All of them have the method que_eres() which writes what class is the object (just for an example):

#include <iostream>
using namespace std;

class persona
{
    public:
    void que_eres() { cout<<"Soy una persona."<<endl; }
};

class estudiante: public persona
{
    public:
    void que_eres()
    {
        cout<<"Soy un estudiante."<<endl;
        persona::que_eres();
    }
};

class empleado: public persona
{
    public:
    void que_eres()
    {
        cout<<"Soy un empleado."<<endl;
        persona::que_eres();
    }
};

class practicante: public estudiante, public empleado
{
    public:
    void que_eres()
    {
        estudiante::que_eres();
        empleado::que_eres();
    }
};

int main()
{
    practicante jose;
    jose.que_eres();
}

The result is:

Soy un estudiante.
Soy una persona.
Soy un empleado.
Soy una persona.

It's not bad, but it would be better if Soy una persona were only written once (that is, that the method que_eres() of the grandfather would be executed just once). Is that possible?

For example, in Python we can do this:

class persona:
    def que_eres(self): 
        print("Soy una persona.")

class estudiante(persona):
    def que_eres(self): 
        print("Soy un estudiante.")
        super().que_eres()

class empleado(persona):
    def que_eres(self): 
        print("Soy un empleado.")
        super().que_eres()

class practicante(estudiante,empleado):
    def que_eres(self): 
        super().que_eres()

jose = practicante()
jose.que_eres()

And the result is just:

Soy un estudiante.
Soy un empleado.
Soy una persona.
Christophe
  • 68,716
  • 7
  • 72
  • 138
Ali Rojas
  • 549
  • 1
  • 3
  • 12
  • 1
    This is a great example of why multiple inheritance is generally not a good idea. – cdhowie Jun 10 '20 at 03:42
  • 3
    Note that your `practicante` is not _a_ `persona`, but is, in fact, _two_ `persona`s. One is a `estudiante`, and one is a `empleado`. – Mooing Duck Jun 11 '20 at 17:51

1 Answers1

3

This is because you have two persona instances in any practicante: one for estudiante and one for empleado and nowhere did you say that it’s always the same persona. (Online demo).

If you want all these these persona to be the same. This is known as the diamond problem. You need to use public virtual inheritance for every intermediary class that inherits the persona.

But there is more to it: since there will be only one virtual base, you’ll need to take care. This means that practicante needs to provide for the persona constructor, since otherwhise the compiler wouldn’t know which of estudiante and empleado invokation of the constructor would have priority. Of course, if you have only the default consturctor this will work almost as is (online demo):

class estudiante: public virtual persona
{
    public:
        ...
};
class empleado: public virtual persona
{
    public:
        ...
};

And similarly, you’ll have to make sure that there is no double invocation by avoiding the mechanics upstream calls that you use, and having the different subclasses cooperate for printing who they but are only once. An easy trick could be to pass a string parameter (default ""s) to que_eres() to build the string to be printed in the calls, and let only the base class printing it. If you don’t want to change the signature of que_eres() you can of course use an auxiliary function.

Here for example an implementation with only one que_eres() in the base class, that uses a virtual quien_soy() that every class overrides to build the presentation string. Virtual bases must then ensure that they appear only once in the string. Online demo:

class persona
{
    protected:
        virtual string quien_soy(string x=""s) {
            const auto s="Soy una persona.\n"s; 
            auto p=x.find(s);
            if (p==string::npos)
                x += s;
            else {
                x = x.substr(0,p)+ x.substr(p+s.size()) +s;
            }
            return x;
        }
    public:
        void que_eres() { cout<<quien_soy() <<endl; }
        virtual ~persona() {}
};

class estudiante: public virtual persona
{
    protected:
        string quien_soy(string x=""s) override
        {
            return persona::quien_soy(x+"Soy un estudiante.\n");
        }
};

class empleado: public virtual persona
{
    protected:
        string quien_soy(string x=""s) override
        {
            return persona::quien_soy(x+"Soy un empleado.\n");
        }
};

class practicante: public estudiante, public empleado
{
    protected:
        string quien_soy(string x=""s) override
        {
            return empleado::quien_soy(estudiante::quien_soy(x+"Soy un praticante, por consiguiente:\n"));
        }
};

All this make multiple inheritance less trivial than it appears at first sight, at least in a diamond case.

Hint: in the real life a persona is a persona. An estudiante and an empleado are not a person but roles that are taken by a person. Composition over inheritance would make this more realistic, leaving the personas the possibility to change role over their life ;-)

Christophe
  • 68,716
  • 7
  • 72
  • 138
  • 1
    I'm pretty sure Solving the diamond inheritance won't fix the effects in the question. – Mooing Duck Jun 11 '20 at 17:42
  • @MooingDuck OP doesn’t know at all the diamond. I’m pretty sure she/he will find a way once the situation is clarified. BTW, I’ve just made an edit with a suggestion of how to handle it. Unfortunately I’m on my way and cannot code on the tiny screen of my phone. Later I may add some POC :-) – Christophe Jun 11 '20 at 17:49
  • I'm very familiar with the diamond problem, and am struggling to find a way (in the generic case) even with that knowledge. I suppose solving the specific case is easy. – Mooing Duck Jun 11 '20 at 17:50
  • @MooingDuck I’m fairly familiar with the diamond problem, as well. It requires a shift of the viewpoint and an approach similar to the constructor issue. This being said I don’t promote it, especially when there’s an easy composition alternative in sight (see hint). – Christophe Jun 11 '20 at 17:54