从父类 ctor 调用重写的方法

2022-09-01 20:37:43

我尝试从父类的构造函数调用重写的方法,并注意到不同语言的不同行为。

C++ - echoes A.foo()

class A{

public: 

    A(){foo();}

    virtual void foo(){cout<<"A.foo()";}
};

class B : public A{

public:

    B(){}

    void foo(){cout<<"B.foo()";}
};

int main(){

    B *b = new B(); 
}

Java - echoes B.foo()

class A{

    public A(){foo();}

    public void foo(){System.out.println("A.foo()");}
}

class B extends A{  

    public void foo(){System.out.println("B.foo()");}
}

class Demo{

    public static void main(String args[]){
        B b = new B();
    }
}

C# - echoes B.foo()

class A{

    public A(){foo();}

    public virtual void foo(){Console.WriteLine("A.foo()");}
}

class B : A{    

    public override void foo(){Console.WriteLine("B.foo()");}
}


class MainClass
{
    public static void Main (string[] args)
    {
        B b = new B();              
    }
}

我意识到,C++对象是从层次结构中最顶层的父级创建的,因此当构造函数调用重写的方法时,B甚至不存在,因此它调用了该方法的A'版本。但是,我不确定为什么我在Java和C#中的行为不同(来自C++)


答案 1

在C++中,正如您正确指出的那样,在构造函数完成之前,对象是类型。对象在其构造过程中实际上会更改类型。这就是为什么使用该类的 vtable,因此调用而不是 。AAAA::foo()B::foo()

在 Java 和 C# 中,派生最多的类型的 vtable(或等效机制)贯穿始终,即使在基类的构造过程中也是如此。所以在这些语言中,被叫到。B.foo()

请注意,通常不建议从构造函数调用虚拟方法。如果您不是很小心,虚拟方法可能会假定对象是完全构造的,即使事实并非如此。在Java中,每个方法都是隐式虚拟的,你别无选择。


答案 2

虽然我知道您这样做是为了实验,但重要的是要注意以下引文来自 Effective Java 2nd Edition, Item 17: Design and Document for Inherit,否则禁止它

类还必须遵守一些限制才能允许继承。构造函数不得直接或间接调用可重写的方法。如果违反此规则,将导致程序失败。超类构造函数在子类构造函数之前运行,因此子类中的重写方法将在子类构造函数运行之前调用。如果重写方法依赖于子类构造函数执行的任何初始化,则该方法的行为将不会按预期方式运行。

下面是一个示例来说明:

public class ConstructorCallsOverride {
    public static void main(String[] args) {
        abstract class Base {
            Base() { overrideMe(); }
            abstract void overrideMe(); 
        }
        class Child extends Base {
            final int x;
            Child(int x) { this.x = x; }
            @Override void overrideMe() {
                System.out.println(x);
            }
        }
        new Child(42); // prints "0"
    }
}

这里,当构造函数调用 时,尚未完成 初始化 ,并且该方法得到错误的值。这几乎肯定会导致错误和错误。BaseoverrideMeChildfinal int x


推荐