¿Cuáles son las reglas para llamar al constructor de superclase?


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

Por ejemplo, sé que en Java, debe hacerlo como la primera línea del constructor de subclase (y si no lo hace, se asume una llamada implícita a un super constructor no-arg, lo que le da un error de compilación si falta).

Author: Melebius, 2008-09-23

9 answers

Los constructores de clase base se llaman automáticamente si no tienen ningún argumento. 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++ soporta herencia múltiple (para bien o para mal), por lo que la clase base debe ser referida 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í.

 773
Author: luke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-08 18:44:25

En C++, los constructores sin argumento para todas las superclases y variables miembro son llamados para usted, antes de ingresar su constructor. Si desea pasarles argumentos, hay una sintaxis separada para esto llamada "encadenamiento del constructor", que se ve así:

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

Si algo se ejecuta en este punto lanza, las bases/miembros que previamente habían completado la construcción tienen sus destructores llamados y la excepción se replantea al llamador. Si desea capturar excepciones durante el encadenamiento, debe usar una función try block:

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 esta forma, 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 capturar 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 catch de función no genera una excepción diferente, el tiempo de ejecución volverá a generar el error original; excepciones durante la inicialización no puede ser ignorado.

 198
Author: puetzk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-01 18:50:52

En C++ hay un concepto de lista de inicialización del constructor, que es donde puede y debe llamar al constructor de la clase base y donde también debe 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, asumiendo 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 se puede ver, el el constructor de la clase base es llamado en la lista de inicialización. Inicializar los miembros de datos en la lista de inicialización, por cierto, es preferible a asignar los valores para b_ y c_ dentro del cuerpo del constructor, porque está ahorrando 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 la clase, independientemente de su orden en la lista de inicialización. Para evitar errores extraños, que puede surgir si sus miembros de 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 la clase. Por la misma razón, el constructor de clase base debe ser el primer elemento de la lista de inicialización. Si lo omite por completo, entonces el constructor predeterminado para la clase base se llamará automáticamente. En ese caso, si la clase base no tiene un constructor predeterminado, obtendrá un error de compilador.

 44
Author: Dima,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-23 13:34:21

La única forma de pasar valores a un constructor padre es a través de una lista de inicialización. La lista de inicialización se implementa con a : y luego una lista de clases y los valores que se pasarán a ese constructor de clases.

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

También recuerde que si tiene un constructor que no toma parámetros en la clase padre, se llamará automáticamente antes de que se ejecute el constructor hijo.

 16
Author: CR.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-23 13:21:28

Si tiene un constructor sin argumentos, será llamado antes de que el constructor de clase derivado sea ejecutado.

Si desea llamar a un constructor base con argumentos, debe escribirlo explícitamente en el constructor derivado de la siguiente 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 padre en C++. Eso sucede automáticamente si es un C'tor no-arg, sucede si llamas al constructor derivado directamente como se muestra arriba o tu código no se compilará.

 16
Author: Nils Pipenbrinck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-26 09:14:32

Todo el mundo mencionó una llamada al constructor a través de una lista de inicialización, pero nadie dijo que el constructor de una clase padre puede ser llamado explícitamente desde el cuerpo del constructor del miembro derivado. Vea la pregunta Llamando a un constructor de la clase base desde un cuerpo constructor de subclase, por ejemplo. El punto es que si usa una llamada explícita a una clase padre o a un constructor de superclase en el cuerpo de una clase derivada, esto en realidad solo está creando una instancia del padre class y no está invocando al constructor de clase padre en el objeto derivado. La única forma de invocar una clase padre 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 clase derivada. Así que tal vez no debería llamarse una "llamada de constructor de superclase". Puse esta respuesta aquí porque alguien podría confundirse (como lo hice yo).

 13
Author: TT_,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:26:38

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

 8
Author: edW,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-15 18:49:38
CDerived::CDerived()
: CBase(...), iCount(0)  //this is the initialisation list. You can initialise member variables here too. (e.g. iCount := 0)
    {
    //construct body
    }
 6
Author: Dynite,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-23 13:24:19

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

 2
Author: darth_coder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-20 02:26:40