Ordre d'appel des constructeurs/destructeurs en héritage

une petite question sur la création d'objets. Dire que j'ai de ces deux classes:

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;
};

et dans le principal je crée une instance de B:

int main(){
    B b;
}

Notez que B dérive de A et aussi un champ de type A.

j'essaie de comprendre les règles. Je sais que lorsque la construction d'un objet appelle d'abord son constructeur parent, et vice versa lors de la destruction.

Qu'en est-il des champs (A a; dans ce cas)? Lorsque B est créé, quand il call A'constructeur? Je n'ai pas défini une liste d'initialisation, est-il une sorte de une liste par défaut? Et si il n'y a pas de liste par défaut? Et la même question sur la destruction.

38
demandé sur Mat 2011-09-24 17:20:34

6 réponses

  • la Construction commence toujours par la base class. S'il y a plusieurs base class es alors, la construction commence avec la base la plus à gauche. (note: S'il y a un virtual héritage alors il est donné la préférence plus élevée).
  • puis les champs membres sont construits. Ils sont initialisés dans le ordonnez qu'ils soient déclarés!--13-->
  • Enfin, le class lui-même est construit
  • l'ordre du destructeur est exactement inverse

Indépendamment de l'initialiseur de liste, l'ordre d'appel sera comme ceci:

  1. Base class Aconstructeur
  2. class B'champ nommé a (de type class A) seront construits
  3. dérivée class Bconstructeur
74
répondu iammilind 2018-02-16 02:12:03

en supposant qu'il n'y ait pas d'héritage virtuel/multiple (ce qui complique les choses un peu) alors les règles sont simples:

  1. la mémoire de l'objet est attribuée
  2. le constructeur des classes de base est exécuté, se terminant par les plus dérivées
  3. l'initialisation du membre est exécutée
  4. L'objet devient une véritable instance de sa classe
  5. le code du Constructeur est exécuté

Une chose importante à retenir est que jusqu'à l'étape 4, l'objet n'est pas encore une instance de sa classe, becuse il gagne ce titre qu'après l'exécution du constructeur commence. Cela signifie que s'il y a une exception lancée pendant le constructeur d'un membre, le destructeur de l'objet n'est pas exécuté, mais seules les pièces déjà construites (par exemple les membres ou les classes de base) seront détruites. Cela signifie aussi que si dans le constructeur d'un membre ou d'une classe de base vous appelez n'importe quelle fonction de membre virtuel de l'objet l'implémentation appelée sera celle de base, pas la dérivée. Une autre chose importante à retenir est que les membres listés dans la liste d'initialisation seront construits dans l'ordre où ils sont déclarés dans la classe, et non dans l'ordre où ils apparaissent dans la liste d'initialisation (heureusement la plupart des compilateurs décents émettront un avertissement si vous énumérez des membres dans un ordre différent de celui de la déclaration de classe).

Notez aussi que même si pendant l'exécution du code du constructeur le this objet déjà gagné sa classe finale (par exemple en ce qui concerne virtual dispatch) le destructeur de la classe ne va pas être appelé à moins que le constructeur achève son exécution. Ce n'est que lorsque le constructeur termine l'exécution que l'instance de l'objet est un véritable citoyen de première classe parmi les instances... avant ce point est seulement une instance "wanna-be" (en dépit d'avoir la classe correcte).

la Destruction se produit dans l'ordre exact inverse: d'abord le destructeur d'objet est exécuté, puis il perd sa classe (i.e. à partir de ce point sur l'objet est considéré comme un objet de base) alors tous les membres sont détruits dans l'ordre inverse de la déclaration et finalement le processus de destruction de classe de base est exécuté jusqu'au parent le plus abstrait. Comme pour le constructeur si vous appelez n'importe quelle fonction de membre virtuel de l'objet (soit directement ou indirectement) dans une base ou un destructeur de membre l'implémentation exécutée sera la parent parce que l'objet a perdu son titre de classe quand la classe destructor compléter.

21
répondu 6502 2015-11-20 10:09:56

les classes de Base sont toujours construites avant les membres de données. Les membres de données sont construits dans l'ordre où ils sont déclarés dans la classe. Cet ordre n'a rien à voir avec la liste d'initialisation. Quand un membre de données est initialisé, il va regarder dans votre liste d'initialisation pour les paramètres, et appeler le constructeur par défaut s'il n'y a pas de correspondance. Les destructeurs pour les données membres sont toujours appelés dans l'ordre inverse.

7
répondu Vaughn Cato 2016-01-07 18:12:27

le constructeur de classe de Base exécute toujours en premier.donc, quand vous écrivez une déclaration B b; le constructeur de A s'appelle d'abord et ensuite le B le constructeur de la classe.par conséquent, la sortie de l'constructeurs sera dans une séquence comme suit:

A() C-tor
A() C-tor
B() C-tor
3
répondu shubhendu mahajan 2011-09-24 14:41:03
#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 réponse dans ce cas est 2531. Comment le constructeur sont appelés ici:

  1. B::A(int n=2) constructeur est appelé
  2. B::B(5) Le constructeur est appelé
  3. B. m_A1:: A (3) s'appelle
  4. B. m_A2:: A (5) s'appelle

de La même façon Destructeur est appelé:

  1. B::~B() s'appelle. I. e m_i = 2, qui décrément m_i à 1 dans A.
  2. B. m_A2::~a() s'appelle. m_i = 5
  3. B. m_A1::~A() est appelé. m_i = 3 4 B::~A () est appelé., m_i = 1

dans cet exemple, la construction de m_A1 & m_A2 n'est pas pertinente de l'ordre de liste d'initialisation mais de leur ordre de déclaration.

1
répondu NEERAJ SWARNKAR 2014-11-09 19:08:05

sortie du code modifié est:

A() C-tor
A() C-tor
B() C-tor
~B() D-tor
~A() D-tor
~A() D-tor
0
répondu Caleb 2011-09-24 13:50:27