resueltos - C++ llamando a los constructores de la clase base
tipos de herencia en java (6)
#include <iostream>
#include <stdio.h>
using namespace std;
// Base class
class Shape
{
public:
void setWidth(int w)
{
width = w;
}
void setHeight(int h)
{
height = h;
}
Shape()
{
printf("creating shape /n");
}
Shape(int h,int w)
{
height = h;
width = w;
printf("creatig shape with attributes/n");
}
protected:
int width;
int height;
};
// Derived class
class Rectangle: public Shape
{
public:
int getArea()
{
return (width * height);
}
Rectangle()
{
printf("creating rectangle /n");
}
Rectangle(int h,int w)
{
printf("creating rectangle with attributes /n");
height = h;
width = w;
}
};
int main(void)
{
Rectangle Rect;
Rect.setWidth(5);
Rect.setHeight(7);
Rectangle *square = new Rectangle(5,5);
// Print the area of the object.
cout << "Total area: " << Rect.getArea() << endl;
return 0;
}
La salida del programa se da a continuación.
creating shape
creating rectangle
creating shape
creating rectangle with attributes
Total area: 35
Al construir los dos objetos de clase derivados, veo que siempre es el constructor de la clase base que se llama primero por defecto. ¿Hay alguna razón para esto? ¿Es esta la razón por la que los lenguajes como Python insisten en llamadas explícitas de constructores de clase base en lugar de llamadas implícitas como C ++?
¿Por qué se llama al constructor por defecto de la clase base? Resulta que no siempre es el caso. Cualquier constructor de la clase base (con diferentes firmas) puede invocarse desde el constructor de la clase derivada. En su caso, se llama al constructor predeterminado porque no tiene parámetros, por lo que es el predeterminado.
Cuando se crea una clase derivada, el orden al que se llama a los constructores es siempre Base -> Derivado en la jerarquía. Si tenemos:
class A {..}
class B : A {...}
class C : B {...}
C c;
Cuando c se crea, primero se invoca el constructor para A, y luego el constructor para B, y luego el constructor para C.
Para garantizar ese orden, cuando se llama a un constructor de clase derivada, siempre invoca al constructor de la clase base antes de que el constructor de la clase derivada pueda hacer cualquier otra cosa. Por esa razón, el programador puede invocar manualmente un constructor de clase base en la única lista de inicialización del constructor de la clase derivada, con los parámetros correspondientes. Por ejemplo, en el siguiente código, el constructor predeterminado de Derived invocará el constructor Base Base :: Base (int i) en lugar del constructor predeterminado.
Derived() : Base(5)
{
}
Si no se invoca tal constructor en la lista de inicialización del constructor de la clase derivada, entonces el programa asume el constructor de una clase base sin parámetros. Esa es la razón por la que se invoca un constructor sin parámetros (es decir, el constructor predeterminado).
Cuando se construyen los objetos, siempre se construye primero el subobjeto de la clase base, por lo tanto, primero se llama al constructor de la clase base y luego se llama a los constructores de la clase derivada. La razón es que los objetos de clase derivados contienen subobjetos heredados de la clase base. Siempre debe llamar al constructor de la clase base para inicializar los subobjetos de la clase base. Generalmente llamamos al constructor de la clase base en la lista de inicialización de miembros de la clase derivada. Si no llama explícitamente al constructor de la clase base, la compilación llamará al constructor predeterminado de la clase base para inicializar el subobjeto de la clase base. Sin embargo, la llamada implícita en el constructor predeterminado no funciona necesariamente en todo momento (por ejemplo, si la clase base define un constructor al que no se podría llamar sin argumentos).
Cuando los objetos están fuera del alcance, primero llamará al destructor de la clase derivada, luego al destructor de la clase base.
En c ++, el compilador siempre se asegura de que las funciones en la jerarquía de objetos se llamen con éxito. Estas funciones son constructores y destructores y la jerarquía de objetos significa árbol de herencia.
De acuerdo con esta regla, podemos suponer que el compilador llamará a los constructores y destructores para cada objeto en la jerarquía de herencia incluso si no lo implementamos. Para realizar esta operación, el compilador sintetizará los constructores y destructores indefinidos para nosotros y los nombraremos como constructores y destructores predeterminados. Luego, el compilador llamará al constructor predeterminado de la clase base y luego al constructor de la clase derivada.
En su caso, no llama al constructor de la clase base, pero el compilador lo hace por usted llamando al constructor predeterminado de la clase base porque si el compilador no lo hizo, su clase derivada, que es Rectangle en su ejemplo, no estará completa y podría causar un desastre porque quizás usará alguna función miembro de la clase base en su clase derivada. Así que por el bien de la seguridad, el compilador siempre necesita todas las llamadas del constructor.
Imagínelo así: cuando su subclase hereda propiedades de una superclase, no aparecen mágicamente. Todavía tienes que construir el objeto. Entonces, llamas al constructor base. Imagínese si su clase hereda una variable, que su constructor de superclase inicializa en un valor importante. Si no hiciéramos esto, su código podría fallar porque la variable no se inicializó.
La respuesta corta para esto es, "porque eso es lo que especifica el estándar de C ++".
Tenga en cuenta que siempre puede especificar un constructor que sea diferente del predeterminado, así:
class Shape {
Shape() {...} //default constructor
Shape(int h, int w) {....} //some custom constructor
};
class Rectangle : public Shape {
Rectangle(int h, int w) : Shape(h, w) {...} //you can specify which base class constructor to call
}
Se llama al constructor predeterminado de la clase base solo si no especifica a cuál llamar.
Se llama al constructor de clase predeterminado a menos que llame explícitamente a otro constructor en la clase derivada. el lenguaje especifica esto
Rectangle(int h,int w):
Shape(h,w)
{...}
Llamaré al otro constructor de la clase base.