2011-09-24 21 views
36

Una pequeña pregunta sobre la creación de objetos. Decir que tengo estas dos clases:Orden de constructores/destructores de llamada en herencia

struct A{ 
    A(){cout << "A() C-tor" << endl;} 
    ~A(){cout << "~A() D-tor" << endl;} 
}; 

struct B : public A{ 
    B(){cout << "B() C-tor" << endl;} 
    ~B(){cout << "~B() D-tor" << endl;} 

    A a; 
}; 

y en el principal se crea una instancia de B:

int main(){ 
    B b; 
} 

Tenga en cuenta que B deriva de A y también tiene un campo de tipo A.

Estoy tratando de descubrir las reglas. Sé que al construir un objeto primero llama a su constructor padre, y viceversa cuando se destruye.

¿Qué pasa con los campos (A a; en este caso)? Cuando se crea B, ¿cuándo llamará al constructor de A? No he definido una lista de inicialización, ¿hay algún tipo de lista predeterminada? Y si no hay una lista predeterminada? Y la misma pregunta sobre la destrucción.

+3

Su ejemplo podría ser más explicativo si su mensaje para el destructor es diferente de su mensaje para el constructor. Además, ¿qué hacen esos 'std :: sort'? – Tom

+0

Además, al experimentar, compare la construcción y la destrucción de 'B b',' B * b = new B(); eliminar b; 'y' A * a = new b(); eliminar a; '(Compare lo que sucede cuando utiliza la palabra clave' virtual' para su destructor, es decir 'virtual ~ A() {cout <<" A D-tor "<< endl;}') – Tom

+0

@Tom, Usted es derecho. Eliminar errores del compilador – iammilind

Respuesta

67
  • La construcción siempre comienza con la base class. Si hay múltiples bases class es entonces, la construcción comienza con la base más a la izquierda. (nota al margen: Si hay una herencia de virtual, se le da una preferencia mayor).
  • Luego se construyen los campos de miembros. Se inicializan en el orden se declaran
  • Por último, el class sí se construye
  • El orden del destructor es exactamente el inverso

Independientemente de la lista de inicialización, el orden de llamada será como esto: campo

  1. class A Base 's constructor
  2. class B' s nombrado a (de tipo class A) se construirá constructor
  3. Derivado class B 's
+2

'Por último, la clase está construida '- ¿estás hablando del cuerpo constructor aquí? – Wolf

+1

@ Wolf: Supongo que sí. – Ludwik

+0

@ Wolf. Se refiere a la clase derivada – MSD561

7

Las clases base siempre están construidas antes de miembros de datos. Los miembros de datos se construyen en el orden en que se declaran en la clase. Esta orden no tiene nada que ver con la lista de inicialización. Cuando se inicializa un miembro de datos, buscará los parámetros en su lista de inicialización y llamará al constructor predeterminado si no hay coincidencia. Los descriptores para los miembros de datos siempre se llaman en el orden inverso.

+1

+1 Concisa, pero buena. Tal vez algún tipo de azúcar atraería más lectores. – Wolf

20

Suponiendo que no hay herencia virtual/múltiple (que complica bastante las cosas), entonces las reglas son simples:

  1. se asigna la memoria de objeto
  2. El constructor de clases base se ejecutan, terminando con más derivada
  3. La inicialización miembro se ha ejecutado
  4. El objeto se convierte en un verdadero ejemplo de su clase
  5. se ejecuta código
  6. Constructor

Una cosa importante para recordar es que hasta el paso 4 el objeto aún no es una instancia de su clase, ya que gana este título solo después de que comienza la ejecución del constructor. Esto significa que si se lanza una excepción durante el constructor de un miembro, el destructor del objeto no se ejecutará, pero solo se destruirán las partes ya construidas (por ejemplo, miembros o clases base). Esto también significa que si en el constructor de un miembro o de una clase base llama a cualquier función de miembro virtual del objeto al que llama la implementación será la base, no la derivada. Otra cosa importante para recordar es que el miembro enumerado en la lista de inicialización se construirá en el orden en que se declaran en la clase, NO en el orden en que aparecen en la lista de inicialización (afortunadamente la mayoría de los compiladores decentes emitirán una advertencia si enumera miembros en un orden diferente de la declaración de clase).

Tenga en cuenta también que, incluso si durante la ejecución de código de constructor del objeto this ya se ganó su última clase (por ejemplo, en lo que respecta a la expedición virtual) al destructor de la clase no va a ser llamado a no ser que el constructor completa su ejecución . Solo cuando el constructor completa la ejecución, la instancia del objeto es un verdadero ciudadano de primera clase entre las instancias ... antes de ese punto es solo una "instancia aspirante" (a pesar de tener la clase correcta).

La destrucción ocurre en el orden inverso exacto: primero se ejecuta el destructor de objeto, luego pierde su clase (es decir, desde este punto el objeto se considera un objeto base) y todos los miembros se destruyen en orden de declaración inversa y finalmente El proceso de destrucción de la clase base se ejecuta hasta el padre más abstracto. En cuanto al constructor, si llama a cualquier función de miembro virtual del objeto (directa o indirectamente) en un destructor base o miembro, la implementación ejecutada será la principal debido a que el objeto perdió su título de clase cuando se completó el destructor de clase.

+0

'a partir de la mayoría de los abstractos' en el punto 2: ¿es esto cierto? – Wolf

+0

@ Wolf: Sí, esto está garantizado. Si tiene la clase 'D' que se deriva de' B', al ejecutar 'D' primero se ejecuta el constructor de' B' (el más abstracto) y luego se ejecuta el constructor 'D'. En realidad, el objeto se convierte en una 'D' real (con respecto a los métodos virtuales) solo después de que se haya completado la construcción de todas las bases y todos los demás miembros (este es el punto complicado de llamar a los métodos virtuales de' D' durante la construcción de uno de los miembros o durante la construcción de un sub-objeto base). – 6502

+0

No es la orden de construcción de las bases derivadas lo que solicité, es la palabra "abstracto" que es engañosa, creo. Como aprendí, una clase abstracta es una clase con al menos un método virtual puro. Por favor, eche un vistazo a [este ejemplo] (http://coliru.stacked-crooked.com/a/2f5e712f8421d303) – Wolf

0

de salida a partir del código modificado es:

A() C-tor 
A() C-tor 
B() C-tor 
~B() D-tor 
~A() D-tor 
~A() D-tor 
3

constructor de la clase base siempre ejecuta first.so cuando se escribe una declaración B b; el constructor de A se llama primero y luego la clase B constructor.therefore la salida de los constructores estarán en una secuencia de la siguiente manera:

A() C-tor 
A() C-tor 
B() C-tor 
1
#include<iostream> 

class A 
{ 
    public: 
    A(int n=2): m_i(n) 
    { 
    // std::cout<<"Base Constructed with m_i "<<m_i<<std::endl; 
    } 
    ~A() 
    { 
    // std::cout<<"Base Destructed with m_i"<<m_i<<std::endl; 
    std::cout<<m_i; 
    } 

    protected: 
    int m_i; 
}; 

class B: public A 
{ 
    public: 
    B(int n): m_a1(m_i + 1), m_a2(n) 
    { 
    //std::cout<<"Derived Constructed with m_i "<<m_i<<std::endl; 
    } 

    ~B() 
    { 
    // std::cout<<"Derived Destructed with m_i"<<m_i<<std::endl; 
    std::cout<<m_i;//2 
    --m_i; 
    } 

    private: 
    A m_a1;//3 
    A m_a2;//5 
}; 

int main() 
{ 
    { B b(5);} 
    std::cout <<std::endl; 
    return 0; 
} 

La respuesta en este caso es 2531. como constructo r se llaman aquí:

  1. B :: A (int n = 2) constructor se llama
  2. B :: B (5) constructor se llama
  3. B.m_A1 :: A (3) se llama
  4. B.m_A2 :: A (5) se llama

La misma-manera Destructor se llama:

  1. B :: ~ B() se llama. es decir, m_i = 2, que disminuye m_i a 1 en A.
  2. B.m_A2 :: ~ Se invoca a (A). m_i = 5
  3. B.m_A1 :: ~ Se llama A(). m_i = 3 4 B :: ~ A) se llama (., m_i = 1

En este ejemplo, la construcción de m_A1 & m_A2 es irrelevante de orden de orden de la lista de inicialización pero su orden de declaración.

Cuestiones relacionadas