Pregunta

¿Cuáles son las reglas de C++ para llamar al constructor de superclase desde uno de subclase?

Por ejemplo, sé que en Java debes hacerlo como la primera línea del constructor de la subclase (y si no lo haces, se supone una llamada implícita a un superconstructor sin argumentos, lo que te dará un error de compilación si falta). .

¿Fue útil?

Solución

Los constructores de clases base se llaman automáticamente si no tienen argumentos.Si desea llamar a un constructor de superclase con un argumento, debe usar la lista de inicialización del constructor de la subclase.A diferencia de Java, C++ admite herencia múltiple (para bien o para mal), por lo que se debe hacer referencia a la clase base por su nombre, en lugar de "super()".

class SuperClass
{
    public:

        SuperClass(int foo)
        {
            // do something with foo
        }
};

class SubClass : public SuperClass
{
    public:

        SubClass(int foo, int bar)
        : SuperClass(foo)    // Call the superclass constructor in the subclass' initialization list.
        {
            // do something with bar
        }
};

Más información sobre la lista de inicialización del constructor. aquí y aquí.

Otros consejos

En C++, los constructores sin argumentos para todas las superclases y variables miembro se llaman automáticamente antes de ingresar a su constructor.Si desea pasarles argumentos, existe una sintaxis separada para esto llamada "encadenamiento de constructores", que se ve así:

class Sub : public Base
{
  Sub(int x, int y)
  : Base(x), member(y)
  {
  }
  Type member;
};

Si se produce algo que se ejecuta en este punto, se llama a los destructores de las bases/miembros que previamente habían completado la construcción y se vuelve a lanzar la excepción a la persona que llama.Si desea detectar excepciones durante el encadenamiento, debe utilizar un bloque de prueba de función:

class Sub : public Base
{
  Sub(int x, int y)
  try : Base(x), member(y)
  {
    // function body goes here
  } catch(const ExceptionType &e) {
    throw kaboom();
  }
  Type member;
};

En este formulario, tenga en cuenta que el bloque try es el cuerpo de la función, en lugar de estar dentro del cuerpo de la función;esto le permite detectar excepciones lanzadas por inicializaciones implícitas o explícitas de miembros y clases base, así como durante el cuerpo de la función.Sin embargo, si un bloque de función catch no genera una excepción diferente, el tiempo de ejecución volverá a generar el error original;excepciones durante la inicialización no puedo ser ignorado.

En C++ existe un concepto de lista de inicialización del constructor, que es donde puedes y debes llamar al constructor de la clase base y donde también debes inicializar los miembros de datos.La lista de inicialización viene después de la firma del constructor, después de dos puntos y antes del cuerpo del constructor.Digamos que tenemos una clase A:


class A : public B
{
public:
  A(int a, int b, int c);
private:
  int b_, c_;
};

Entonces, suponiendo que B tiene un constructor que toma un int, el constructor de A puede verse así:


A::A(int a, int b, int c) 
  : B(a), b_(b), c_(c) // initialization list
{
  // do something
}

Como puede ver, el constructor de la clase base se llama en la lista de inicialización.Por cierto, es preferible inicializar los miembros de datos en la lista de inicialización a asignar los valores para b_ y c_ dentro del cuerpo del constructor, porque se ahorra el costo adicional de la asignación.

Tenga en cuenta que los miembros de datos siempre se inicializan en el orden en que se declaran en la definición de clase, independientemente de su orden en la lista de inicialización.Para evitar errores extraños que pueden surgir si los miembros de sus datos dependen unos de otros, siempre debe asegurarse de que el orden de los miembros sea el mismo en la lista de inicialización y en la definición de clase.Por la misma razón, el constructor de la clase base debe ser el primer elemento en la lista de inicialización.Si lo omite por completo, se llamará automáticamente al constructor predeterminado de la clase base.En ese caso, si la clase base no tiene un constructor predeterminado, obtendrá un error del compilador.

Todo el mundo mencionó una llamada al constructor a través de una lista de inicialización, pero nadie dijo que se pueda llamar explícitamente al constructor de una clase principal desde el cuerpo del constructor del miembro derivado.ver la pregunta Llamar a un constructor de la clase base desde el cuerpo del constructor de una subclase, Por ejemplo.El punto es que si usa una llamada explícita a una clase principal o un constructor de superclase en el cuerpo de una clase derivada, esto en realidad es solo crear una instancia de la clase principal y no invocar el constructor de la clase principal en el objeto derivado. .La única forma de invocar una clase principal o un constructor de superclase en un objeto de clase derivada es a través de la lista de inicialización y no en el cuerpo del constructor de la clase derivada.Entonces tal vez no debería llamarse "llamada al constructor de superclase".Pongo esta respuesta aquí porque alguien podría confundirse (como lo hice yo).

La única forma de pasar valores a un constructor principal es mediante una lista de inicialización.La lista de inicialización se implementa con:y luego una lista de clases y los valores que se pasarán al constructor de esa clase.

Class2::Class2(string id) : Class1(id) {
....
}

Recuerde también que si tiene un constructor que no toma parámetros en la clase principal, se llamará automáticamente antes de que se ejecute el constructor secundario.

Si tiene un constructor sin argumentos, se llamará antes de que se ejecute el constructor de la clase derivada.

Si desea llamar a un constructor base con argumentos, debe escribirlo explícitamente en el constructor derivado de esta manera:

class base
{
  public:
  base (int arg)
  {
  }
};

class derived : public base
{
  public:
  derived () : base (number)
  {
  }
};

No se puede construir una clase derivada sin llamar al constructor principal en C++.Eso sucede automáticamente si es un C'tor sin argumentos, sucede si llama al constructor derivado directamente como se muestra arriba o su código no se compilará.

Si tiene parámetros predeterminados en su constructor base, la clase base se llamará automáticamente.

using namespace std;

class Base
{
    public:
    Base(int a=1) : _a(a) {}

    protected:
    int _a;
};

class Derived : public Base
{
  public:
  Derived() {}

  void printit() { cout << _a << endl; }
};

int main()
{
   Derived d;
   d.printit();
   return 0;
}

La salida es:1

CDerived::CDerived()
: CBase(...), iCount(0)  //this is the initialisation list. You can initialise member variables here too. (e.g. iCount := 0)
    {
    //construct body
    }

Nadie mencionó la secuencia de llamadas al constructor cuando una clase deriva de varias clases.La secuencia es la mencionada al derivar las clases.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top