Вызов переопределенного метода от родительского класса ctor

Я попытался назвать переопределенный метод от конструктора родительского класса и заметил другое поведение через языки.

C++ - эхо 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 - эхо 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# - эхо 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 даже не существует, таким образом, это называет' версию метода. Однако я не уверен, почему я получаю другое поведение в Java и C# (от C++)

21
задан Vaibhav Bajpai 31 July 2011 в 06:12
поделиться

2 ответа

Хотя я понимаю, что вы делаете это для экспериментов, важно отметить следующую цитату из Эффективное 2-е издание Java, пункт 17: Дизайн и документ для наследования, иначе запретить it :

Есть еще несколько ограничений, которым должен подчиняться класс, чтобы разрешить наследование. Конструкторы не должны вызывать заменяемые методы прямо или косвенно. Если вы нарушите это правило, произойдет сбой программы. Конструктор суперкласса запускается перед конструктором подкласса, поэтому метод переопределения в подклассе будет вызываться до запуска конструктора подкласса. Если метод переопределения зависит от любой инициализации, выполняемой конструктором подкласса, метод не будет вести себя должным образом.

Вот пример для иллюстрации:

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"
    }
}

Здесь, когда конструктор Base вызывает overrideMe , Child не завершил инициализацию final int x , и метод получает неверное значение. Это почти наверняка приведет к ошибкам и ошибкам.

9
ответ дан 29 November 2019 в 20:59
поделиться

В C++, как вы правильно заметили, объект имеет тип A до завершения работы конструктора A. На самом деле объект меняет тип во время своего построения. Поэтому используется vtable класса A, так что A::foo() вызывается вместо B::foo().

В Java и C# vtable (или эквивалентный механизм) наиболее производного типа используется повсеместно, даже при построении базовых классов. Поэтому в этих языках вызывается B.foo().

Обратите внимание, что обычно не рекомендуется вызывать виртуальный метод из конструктора. Если вы не будете очень осторожны, виртуальный метод может предположить, что объект полностью построен, хотя это не так. В Java, где каждый метод является неявно виртуальным, у вас нет выбора.

26
ответ дан 29 November 2019 в 20:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: