Quelles sont les règles pour appeler le constructeur de la superclasse?

quelles sont les règles C++ pour appeler le constructeur de superclasses à partir d'une sous-classe?

par exemple, je sais Qu'en Java, vous devez le faire comme première ligne du constructeur de la sous-classe (et si vous ne le faites pas, un appel implicite à un super - constructeur no-arg est supposé-vous donnant une erreur de compilation si cela manque).

566
demandé sur Melebius 2008-09-23 17:09:20

9 réponses

Les constructeurs de classe de Base

sont automatiquement appelés pour vous s'ils n'ont pas d'argument. Si vous voulez appeler un constructeur superclass avec un argument, vous devez utiliser la liste d'initialisation du constructeur de la sous-classe. Contrairement à Java, C++ supporte les héritages multiples (pour le meilleur et pour le pire), de sorte que la classe de base doit être appelée par son nom, plutôt que "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
        }
};

plus d'informations sur la liste d'initialisation du constructeur ici et ici .

773
répondu luke 2012-05-08 18:44:25

dans C++, les constructeurs sans argument pour toutes les variables superclasses et membres sont appelés pour vous, avant d'entrer votre constructeur. Si vous voulez passer les arguments, il y a une syntaxe séparée pour cela appelé "constructor chaining", qui ressemble à ceci:

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

si quelque chose tourne à ce point jette, les bases/membres qui avaient précédemment terminé la construction ont leurs destructeurs appelés et l'exception est repensée à l'appelant. Si vous voulez attraper des exceptions pendant le chaînage, vous devez utiliser une fonction essayer le bloc:

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

dans cette forme, notez que le bloc d'essai est le corps de la fonction, plutôt que d'être à l'intérieur du corps de la fonction; cela lui permet de saisir les exceptions lancées par des initialisations implicites ou explicites de membre et de classe de base, aussi bien que pendant le corps de la fonction. Cependant, si un bloc de capture de fonction ne jette pas une exception différente, le runtime va revenir sur l'erreur d'origine; les exceptions lors de l'initialisation ne peuvent pas être ignorées.

198
répondu puetzk 2016-04-01 18:50:52

dans C++ il y a un concept de liste d'initialisation du constructeur, qui est où vous pouvez et devriez appeler le constructeur de la classe de base et où vous devriez aussi initialiser les membres de données. La liste d'initialisation vient après la signature du constructeur suivant un deux-points, et avant le corps du constructeur. Disons que nous avons une classe A:


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

alors, en supposant que B ait un constructeur qui prend un int, le constructeur de A peut ressembler à ceci:


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

Comme vous pouvez le voir, le constructeur de la classe de base est appelé dans la liste d'initialisation. Initialiser les membres de données dans la liste d'initialisation, soit dit en passant, est préférable à assigner les valeurs pour b_, et c_ à l'intérieur du corps du constructeur, parce que vous économisez le coût supplémentaire de l'assignation.

gardez à l'esprit, que les membres de données sont toujours initialisés dans l'ordre dans lequel ils sont déclarés dans la définition de classe, indépendamment de leur l'ordre dans la liste d'initialisation. Pour éviter les bugs étranges, qui peuvent survenir si vos membres de données dépendent les uns des autres, vous devez toujours vous assurer que l'ordre des membres est le même dans la liste d'initialisation et la définition de classe. Pour la même raison, le constructeur de classe de base doit être le premier élément dans la liste d'initialisation. Si vous l'omettez complètement, alors le constructeur par défaut pour la classe de base sera appelé automatiquement. Dans ce cas, si la classe de base n'ont pas de constructeur par défaut, vous obtiendrez une erreur de compilation.

44
répondu Dima 2008-09-23 13:34:21

la seule façon de passer des valeurs à un constructeur parent est de passer par une liste d'initialisation. La liste d'initialisation est implémentée avec un : puis une liste de classes et les valeurs à passer à ce constructeur de classes.

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

rappelez-vous aussi que si vous avez un constructeur qui ne prend aucun paramètre sur la classe parent, il sera appelé automatiquement avant l'exécution du constructeur enfant.

16
répondu CR. 2008-09-23 13:21:28

si vous avez un constructeur sans arguments, il sera appelé avant que le constructeur de classe dérivé soit exécuté.

si vous voulez appeler un constructeur de base avec des arguments, vous devez écrire explicitement que dans le constructeur dérivé comme ceci:

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

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

vous ne pouvez pas construire une classe dérivée sans appeler le constructeur de parents en C++. Cela se produit soit automatiquement si c'est un non-arg C'TOR, il se produit si vous appelez le le constructeur dérivé directement comme montré ci-dessus ou votre code ne sera pas compilé.

16
répondu Nils Pipenbrinck 2011-07-26 09:14:32

tout le monde a mentionné un appel de constructeur à travers une liste d'initialisation, mais personne n'a dit que le constructeur d'une classe mère peut être appelé explicitement à partir du corps du constructeur du membre dérivé. Voir la question appelant un constructeur de la classe de base d'une sous-classe' corps du constructeur , par exemple. Le point est que si vous utilisez un appel explicite à une classe mère ou à un constructeur de super-classe dans le corps d'une classe dérivée, c'est en fait juste créer un instance de la classe parent et elle n'invoque pas le constructeur de la classe parent sur l'Objet dérivé. La seule façon d'invoquer une classe mère ou un constructeur de super classes sur un objet de classe dérivée est de passer par la liste d'initialisation et non par le corps du constructeur de classe dérivé. Donc peut-être qu'il ne devrait pas être appelé un "appel de constructeur de superclasse". J'ai mis cette réponse ici parce que quelqu'un pourrait devenir confus (comme je l'ai fait).

13
répondu TT_ 2017-05-23 12:26:38

si vous avez des paramètres par défaut dans votre constructeur de base, la classe de base sera appelée automatiquement.

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

sortie: 1

8
répondu edW 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
répondu Dynite 2008-09-23 13:24:19

personne n'a mentionné la séquence des appels du constructeur lorsqu'une classe dérive de classes multiples. La séquence est telle que mentionnée tout en dérivant les classes.

2
répondu darth_coder 2016-11-20 02:26:40