java c++ inheritance

¿Por qué la herencia se comporta de manera diferente en Java y C++ con superclases que llaman(o no) los métodos de las subclases?



inheritance (2)

He escrito, lo que parecía ser, exactamente el mismo ejemplo de herencia tanto en Java como en C ++. Estoy realmente sorprendido de ver los diferentes resultados de estos programas. Permítanme compartir ambos fragmentos de código y las salidas correspondientes.

Código C ++:

class A { public: A() {} void sleep() { cout << "A.Sleep" << endl; eat(); } void eat() {cout << "A.Eat" << endl;} }; class B: public A { public: B() {} void sleep() { A::sleep(); cout << "B.Sleep " <<endl; this->eat(); } void eat() { cout << "B.Eat" << endl; run(); } void run() { A::sleep(); cout << "B.run" << endl; } }; int main() { B *b = new B(); b->sleep(); }

Salida:

A.Sleep A.Eat B.Sleep B.Eat A.Sleep A.Eat B.run executed successfully...

Código Java:

class A { A() {} void sleep() { System.out.println("A.Sleep"); this.eat(); } void eat() { System.out.println("A.Eat");} }; class B extends A { B() {} @Override void sleep() { super.sleep(); System.out.println("B.Sleep"); this.eat(); } @Override void eat() { System.out.println("B.Eat"); run(); } void run() { super.sleep(); System.out.println("B.Run"); } } public class Test { public static void main(String[] args) { B b = new B(); b.sleep(); } }

Salida:

A.Sleep B.Eat A.Sleep B.Eat A.Sleep ...... ...... ...... (Exception in thread "main" java.lang.StackOverflowError)

No sé por qué estos dos ejemplos de herencia se comportan de manera diferente. ¿No debería funcionar de manera similar? Tengo bastante curiosidad por saber ... ¿cuál es la explicación de este escenario?


En su ejemplo de C ++, está hiding los métodos básicos, pero no los reemplaza. Así que en realidad son métodos diferentes que simplemente tienen el mismo nombre. Si estas llamando

A* a = new B(); a->sleep();

En realidad se imprimirá "A.Sleep" . Si desea anular un método, debe declararlo virtual en la clase Base (automáticamente haciéndolo virtual en todas las subclases también). Puede leer más acerca de la función que oculta vs anular en C ++ en esta publicación .

En su ejemplo de Java, en realidad reemplaza los métodos, por lo que son el mismo método. Uno que toma el lugar de lo viejo. Puede pensarlo de esta manera: todas las funciones de Java están marcadas en secreto como virtual , lo que significa que se pueden anular. Si desea que un método no se pueda anular en Java, debe declararlo final .


Nota: ten cuidado, cada lenguaje como su propia manera de pensar . Hay muchas formas de interpretar / implementar OO. Incluso si C ++ y Java parecen similares, están lejos de ser similares.

En ambos idiomas, el compilador verifica en tiempo de compilación si puede llamar a un método, examinando la clase (y la heredada de la actual, etc.) para un método de la firma y visibilidad correctas. Lo que hace que las cosas sean diferentes es la forma en que realmente se emite la llamada.

C ++ :

En el caso de métodos no virtuales, el método llamado se determina completamente en tiempo de compilación . Por esta razón, incluso si el objeto es de clase B , cuando ejecuta A::sleep la llamada a eat se resuelve como una llamada a A::eat ( eat no es virtual, entonces el compilador llama a A::eat porque está en nivel A ). En B::sleep() la llamada a this->eat() se resuelve como una llamada a B.eat() porque en ese lugar es de tipo B No puede ir a la jerarquía de herencia (llamar a eat en clase A nunca llamará a un método de eat en una clase a continuación).

Tenga en cuenta que las cosas son diferentes en el caso de los métodos virtuales (es más similar al caso de Java y es diferente).

Java :

En Java, el método llamado se determina en tiempo de ejecución y es el que está más relacionado con la instancia del objeto. Por lo tanto, cuando A.sleep en A.sleep la llamada a eat será una llamada relacionada con el tipo del objeto actual, es decir, del tipo B (porque el objeto actual es de tipo B ), se B.eat .

Entonces tiene un desbordamiento de pila porque, mientras juega con un objeto de tipo B una llamada a B.sleep() llamará a A.sleep() , que llamará a B.eat() , que a su vez llamará a B.run() que llamará a A.sleep() , etc. en un bucle que nunca termina.