【问题标题】:How to make it so parent classes don't repeat a grandfather method that was already executed如何使父类不重复已经执行的祖父方法
【发布时间】:2020-09-29 08:35:05
【问题描述】:

我们有一个子类practicante,它继承自两个类:estudianteempleado,它们都继承自祖父类persona。它们都有 que_eres() 方法,它写出对象是什么类(仅作为示例):

#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();
}

结果是:

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

还不错,不过如果Soy una persona只写一次会更好(即祖父的方法que_eres()只执行一次)。这可能吗?

例如,在 Python 中我们可以这样做:

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()

结果就是:

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

【问题讨论】:

  • 这是一个很好的例子,说明了为什么多重继承通常不是一个好主意。
  • 请注意,您的 practicante 不是 a persona,但实际上是 两个 personas。一个是estudiante,一个是empleado

标签: c++ inheritance multiple-inheritance class-design diamond-problem


【解决方案1】:

这是因为您在任何practicante 中都有两个persona 实例:一个用于estudiante,一个用于empleado,而您并没有说它总是相同的persona。 (Online demo)。

如果您希望所有这些persona 相同。这被称为diamond problem。您需要为每个继承persona 的中间类使用public virtual 继承。

但还有更多:由于只有一个虚拟基地,因此您需要小心。这意味着practicante 需要提供persona 构造函数,否则编译器将不知道构造函数的调用estudianteempleado 中的哪一个具有优先权。当然,如果你只有默认的构造函数,这几乎可以正常工作(online demo):

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

同样,您必须通过避免使用的机制上游调用来确保没有重复调用,并让不同的子类合作打印他们只有一次的身份。一个简单的技巧可能是将字符串参数(默认为""s)传递给que_eres() 以构建要在调用中打印的字符串,并让基类打印它。如果您不想更改que_eres() 的签名,当然可以使用辅助功能。

例如,基类中只有一个que_eres() 的实现,它使用每个类都覆盖的虚拟quien_soy() 来构建表示字符串。虚拟碱基必须确保它们在字符串中只出现一次。 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"));
        }
};

所有这些都使得多重继承不像第一眼看上去那么简单,至少在菱形案例中是这样。

提示:在现实生活中,角色就是角色。一个 estudiante 和一个 empleado 不是一个人,而是一个人所扮演的角色。 Composition over inheritance 会让这更现实,让角色有可能在他们的生活中改变角色;-)

【讨论】:

  • 我很确定解决钻石继承问题不会解决问题中的影响。
  • @MooingDuck OP 根本不知道钻石。我敢肯定,一旦情况得到澄清,她/他会找到办法的。顺便说一句,我刚刚进行了编辑,并提出了如何处理它的建议。不幸的是,我正在路上,无法在手机的小屏幕上编码。稍后我可能会添加一些 POC :-)
  • 我对钻石问题非常熟悉,即使有了这些知识,我也很难找到一种方法(在一般情况下)。我想解决具体案例很容易。
  • @MooingDuck 我也非常熟悉钻石问题。它需要转变观点和类似于构造函数问题的方法。话虽这么说,我不会宣传它,尤其是当有一个简单的组合替代方案时(见提示)。
猜你喜欢
  • 2012-09-17
  • 2011-02-04
  • 1970-01-01
  • 2012-08-09
  • 2011-06-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多