Вызов полиморфной функции из конструктора - это путь к катастрофе в большинстве языков OO. В этой ситуации разные языки будут работать по-разному.
Основная проблема заключается в том, что во всех языках базовый тип (типы) должен быть создан до типа Derived. Теперь проблема в том, что значит вызывать полиморфный метод из конструктора. Как вы ожидаете, что он будет вести себя как? Существует два подхода: вызвать метод на базовом уровне (стиль C ++) или вызвать полиморфный метод для неструктурированного объекта в нижней части иерархии (способ Java).
В C ++ базовый класс создаст свою версию таблицы виртуальных методов перед вводом своей собственной конструкции. На этом этапе вызов виртуального метода в конечном итоге вызовет базовую версию метода или создаст чисто виртуальный метод с именем в случае, если он не имеет реализации на этом уровне иерархии. После того, как Base будет полностью построен, компилятор начнет создавать класс Derived и переопределит указатели на методы, чтобы указывать на реализации на следующем уровне иерархии.
class Base {
public:
Base() { f(); }
virtual void f() { std::cout << "Base" << std::endl; }
};
class Derived : public Base
{
public:
Derived() : Base() {}
virtual void f() { std::cout << "Derived" << std::endl; }
};
int main() {
Derived d;
}
// outputs: "Base" as the vtable still points to Base::f() when Base::Base() is run
В Java компилятор создаст эквивалент виртуальной таблицы на самом первом этапе построения, прежде чем вводить конструктор Base или конструктор Derived. Последствия разные (и на мой взгляд более опасные). Если конструктор базового класса вызывает метод, который переопределяется в производном классе, вызов будет фактически обработан на производном уровне, вызывая метод для неструктурированного объекта, что приведет к неожиданным результатам. Все атрибуты производного класса, которые инициализируются внутри блока конструктора, еще не инициализированы, включая атрибуты 'final'. Элементы, которые имеют значение по умолчанию, определенное на уровне класса, будут иметь это значение.
public class Base {
public Base() { polymorphic(); }
public void polymorphic() {
System.out.println( "Base" );
}
}
public class Derived extends Base
{
final int x;
public Derived( int value ) {
x = value;
polymorphic();
}
public void polymorphic() {
System.out.println( "Derived: " + x );
}
public static void main( String args[] ) {
Derived d = new Derived( 5 );
}
}
// outputs: Derived 0
// Derived 5
// ... so much for final attributes never changing :P
Как видите, вызов полиморфных ( виртуальных в терминологии C ++) распространенных источников ошибок. В C ++, по крайней мере, у вас есть гарантия, что он никогда не вызовет метод для еще не построенного объекта ...