多继承方法解决:C++与Python的比较

3
我看到 C++ 和 Python 在处理菱形继承场景时表现非常不同。
对于以下两个代码片段,逻辑相同但输出不同。我不明白为什么。如果有人能提供一些见解,我将不胜感激。
Python 输出“B B”。
class A(object):
    def foo(self):
        print 'A'

class B(A):
    def foo(self):
        print 'B'
    def bar(self):
        self.foo()

class C(A):
    def foo(self):
        print 'C'
    def bar(self):
        self.foo()

class D(B, C):
    def bar(self):
        B.bar(self)
        C.bar(self)

D().bar()

但是C++会输出 "B C"。
struct A
{   
    virtual void foo() {
        cout << "A" << endl;
    }   
};  

struct B : public A
{   
    virtual void foo() {
        cout << "B" << endl;
    }   

    virtual void bar() { this->foo(); }
};  

struct C : public A
{   
    virtual void foo() {
        cout << "C" << endl;
    }   

    virtual void bar() { this->foo(); }
};  

struct D : public B, public C
{   
    virtual void bar() {
        B::bar();
        C::bar();
    }   
};  

int main()
{   
    D().bar();
} 

当我在C++中的每个bar()方法中添加cout << typeid(*this).name() << endl;这一行时,将会打印出以下内容:
B 0x7fff803d9b70
C 0x7fff803d9b78
D 0x7fff803d9b70

看起来BD共享同一地址,但C在另一个虚表中。

编辑

如回复@JoranBeasley所建议的,我尝试对Python代码进行以下检查:

D.foo == B.foo
D.foo == C.foo
D.foo is B.foo
D.foo is C.foo

Python 2.7 输出 True False False False

Python 3.4 输出 True False True False

看起来在 Python 2 和 3 中,B.fooD.foo 都是相同的方法,但是 Python 2 会复制,而 Python 3 则不会。


如果你想在C++中实现类似Python的继承,你需要使用虚拟继承。 - o11c
2
这个 C++ 不是真正的钻石:D 有两个不同继承的 A 基类。 - rodrigo
@o11c:实际上我想要相反的方式:更喜欢C++的行为。 - Ainz Titor
@JimJarvis 在Python中,除非您使用元类技巧来生成基类的副本,否则这是不可能实现的。这很可能会让每个人感到困惑并破坏各种事情。 - o11c
好问题。我本来期望的是像C++一样是B、C。很高兴能够了解这些关于Python的事情,感谢@Joran Beasley的回答。 - tenwest
显示剩余2条评论
1个回答

4

我只能解释Python的部分...

C.bar(self)

将您创建的D实例作为self传递。

因此,当它调用self.foo()时,与在D.bar中说self.foo()没有区别(即self保持静态相同)

由于继承是从右到左进行的,B.foo遮盖了C.foo

D.foo is B.foo # should print True
D.foo is C.foo # should print false

您当然可以随时致电,不过这里提供一些更方便的选择。
C.foo(self) #prints C as expected

这与Python多重继承方法解析顺序无关,因为它是通过super()调用处理的...相反,您正在尝试显式调用父类方法,而不是允许Python隐式地为您解析它们。

至于为什么它与C ++不同,那是因为它是两种不同的语言,以不同的方式实现了继承和多重继承...

如评论中所述,您可以更改D的定义为

class D(C,B):
     ...

那么D().foo()将会打印出C


1
还要提到,如果他改变继承的顺序(class D(B, C):)为(class D(C, B):),它将打印出 C C ;)(在您解释了从右到左的继承之后) - Bhargav Rao
谢谢你的回答。但是这两行代码都打印出了“False”。 - Ainz Titor
1
@JimJarvis 哦,原来如此。Joran的答案是用Python3编写的。 - Bhargav Rao
1
我确定这与Python3中继承object有关,它是自动完成的...但在Python2中必须显式声明。 - Joran Beasley
@BhargavRao 它打印出 "A A" ... 而 Python 3 打印出 "C C"。我听说过这个 Python 对象机制的变化,但从未见过它实际运作。 - Ainz Titor
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接