Question

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

Par exemple, je sais qu'en Java, vous devez le faire en tant que première ligne du constructeur de la sous-classe (et si vous ne le faites pas, un appel implicite à un constructeur super no-arg est supposé, ce qui vous donne une erreur de compilation si ça manque).

Était-ce utile?

La solution

Les constructeurs de classe de base sont automatiquement appelés pour vous s'ils n'ont pas d'argument. Si vous souhaitez appeler un constructeur de superclasse avec un argument, vous devez utiliser la liste d'initialisation du constructeur de la sous-classe. Contrairement à Java, C ++ prend en charge l'héritage multiple (pour le meilleur ou pour le pire), de sorte que la classe de base doit être référencée par son nom, plutôt que par "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 .

Autres conseils

En C ++, les constructeurs sans argument de toutes les superclasses et de toutes les variables membres sont appelés pour vous avant d'entrer dans votre constructeur. Si vous souhaitez leur transmettre des arguments, il existe une syntaxe distincte pour cet élément appelé "constructeur chaînant", qui ressemble à ceci:

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

Si quelque chose se lance à ce stade, les destructeurs sont appelés pour les bases / membres qui avaient précédemment terminé la construction et l'exception est renvoyée à l'appelant. Si vous voulez intercepter des exceptions lors du chaînage, vous devez utiliser une fonction 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;
};

Dans ce formulaire, notez que le bloc try est le corps de la fonction, plutôt que d'être à l'intérieur du corps de la fonction; Cela lui permet de détecter les exceptions générées par les initialisations implicites ou explicites des membres et des classes de base, ainsi que pendant le corps de la fonction. Cependant, si un bloc d'interception de fonction ne déclenche pas une exception différente, le moteur d'exécution rediffuse l'erreur d'origine. les exceptions lors de l'initialisation ne peuvent pas être ignorées.

En C ++, il existe un concept de liste d'initialisation de constructeur, qui vous permet d'appeler le constructeur de la classe de base et d'initialiser les membres de données. La liste d'initialisation vient après la signature du constructeur après un signe 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_;
};

Ensuite, 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 constater, le constructeur de la classe de base est appelé dans la liste d’initialisation. Au fait, il est préférable d’initialiser les données membres de la liste d’initialisation plutôt que d’affecter les valeurs de b_ et c_ à l’intérieur du corps du constructeur, car vous économiserez le coût supplémentaire de l’attribution.

N'oubliez pas que les membres de données sont toujours initialisés dans l'ordre dans lequel ils ont été déclarés dans la définition de la classe, quel que soit leur 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 dans la définition de la classe. Pour la même raison, le constructeur de la classe de base doit être le premier élément de la liste d'initialisation. Si vous l'omettez complètement, le constructeur par défaut de la classe de base sera appelé automatiquement. Dans ce cas, si la classe de base n’a pas de constructeur par défaut, vous obtiendrez une erreur de compilation.

Tout le monde a mentionné un appel de constructeur via une liste d'initialisation, mais personne n'a dit que le constructeur d'une classe parente pouvait être appelé explicitement à partir du corps du constructeur du membre dérivé. Voir la question Appel d'un constructeur de la classe de base à partir du corps du constructeur d'une sous-classe , par exemple. Le fait est que si vous utilisez un appel explicite à un constructeur de classe parent ou de super-classe dans le corps d'une classe dérivée, il ne s'agit en fait que de créer une instance de la classe parent et ne pas appeler le constructeur de la classe parent sur l'objet dérivé. . Le seul moyen d'appeler un constructeur de classe parent ou de super classe sur un objet de classe dérivée consiste à utiliser la liste d'initialisation et non dans le corps du constructeur de la classe dérivée. Alors peut-être que cela ne devrait pas être appelé un "appel de constructeur de super-classe". Je mets cette réponse ici parce que quelqu'un pourrait être confus (comme je l'ai fait).

Le seul moyen de transmettre des valeurs à un constructeur parent consiste à utiliser une liste d'initialisation. La liste d’initialisation est implémentée avec a: puis une liste de classes et les valeurs à transmettre au constructeur de cette classe.

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

N'oubliez pas non plus que si un constructeur ne prend aucun paramètre sur la classe parente, il sera appelé automatiquement avant l'exécution du constructeur enfant.

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

Si vous voulez appeler un constructeur de base avec des arguments, vous devez l'écrire explicitement 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 automatiquement s'il s'agit d'un C'tor sans argument, cela se produit si vous appelez directement le constructeur dérivé comme indiqué ci-dessus ou si votre code ne sera pas compilé.

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

La sortie est: 1

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

Personne n'a mentionné la séquence d'appels de constructeur lorsqu'une classe dérive de plusieurs classes. La séquence est telle que mentionnée lors de la dérivation des classes.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top