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


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

Par exemple, je sais 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 sans-argument est supposé - vous donner une erreur de compilation si cela manque) .


549
2017-09-23 13:09


origine


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 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), donc 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.


755
2017-09-23 13:18



En C ++, les constructeurs sans argument pour toutes les superclasses et variables membres sont appelés pour vous, avant d'entrer dans votre constructeur. Si vous voulez leur passer des arguments, il existe une syntaxe distincte pour ce que l'on appelle "constructor chaining", qui ressemble à ceci:

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

Si quelque chose est lancé à ce point, les bases / membres qui ont terminé la construction ont leurs destructeurs appelés et l'exception est renvoyée à l'appelant. Si vous voulez attraper des exceptions pendant le 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 d'intercepter les exceptions levées par les initialisations de membre et de classe de base implicite ou explicite, ainsi que pendant le corps de la fonction. Cependant, si un bloc de catch de fonction ne lance pas une exception différente, le runtime renverra l'erreur d'origine; exceptions lors de l'initialisation ne peux pas Etre ignoré.


190
2017-09-23 13:22



En C ++ il y a un concept de la liste d'initialisation du constructeur, où vous pouvez et devez appeler le constructeur de la classe de base et où vous devriez également 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 a 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. D'ailleurs, l'initialisation des membres de données dans la liste d'initialisation est préférable à l'affectation des valeurs pour b_ et c_ dans le corps du constructeur, car vous économisez le coût supplémentaire de l'affectation.

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 ordre dans la liste d'initialisation. Pour éviter les bogues é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 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.


41
2017-09-23 13:34



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

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

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


16
2017-09-23 13:19



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 explicitement écrire cela 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 des parents en C ++. Cela se produit automatiquement si c'est un C'tor non-arg, cela arrive si vous appelez le constructeur dérivé directement comme montré ci-dessus ou votre code ne compilera pas.


15
2018-01-27 22:04



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 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 d'un corps de constructeur d'une sous-classe, par exemple. Le fait est que si vous utilisez un appel explicite à une classe parente ou un constructeur de super classe dans le corps d'une classe dérivée, cela crée simplement une instance de la classe parente et n'invoque pas le constructeur de classe parente sur l'objet dérivé . La seule façon d'invoquer un constructeur de classe parent ou super classe sur un objet de classe dérivée se fait via la liste d'initialisation et non dans le corps du constructeur de la classe dérivée. Alors 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 être confus (comme je l'ai fait).


12
2017-07-15 18:49



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


8
2017-09-23 13:24



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

6
2018-03-12 10:46



Personne n'a mentionné la séquence des appels constructeurs lorsqu'une classe dérive de plusieurs classes. La séquence est comme mentionné tout en dérivant les classes.


2