859 votes

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

Quelles sont les règles C++ pour appeler le constructeur de la classe de base depuis une classe dérivée ?

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 super constructeur sans argument est supposé - ce qui vous donne une erreur de compilation si cela manque).

21 votes

Je ne fais que pinailler : Il n'y a pas de "super classe" en C++, en fait, la norme n'en parle pas du tout. Cette formulation provient de Java (très probablement). Utilisez "classe de base" en C++. Je suppose que super implique un seul parent, alors que le C++ permet un héritage multiple.

0 votes

@andreee je red que un super class est également appelé base class et aussi par exemple dans la boîte à outils qt parent class - dans cet ordre un sub class est également appelé child class Peut-être que cela aide à combattre une certaine confusion potentielle dans la terminologie.

1157voto

luke Points 16255

Les constructeurs des classes 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, le C++ prend en charge l'héritage multiple (pour le meilleur et pour le pire), de sorte que la classe de base doit être désigné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'infos sur la liste d'initialisation du constructeur aquí y aquí .

57 votes

J'ai supprimé "explicit" du constructeur de la SuperClass. Bien qu'il s'agisse d'une bonne pratique pour les constructeurs à argument unique, il n'était pas pertinent pour la discussion en cours. Pour plus d'informations sur le mot clé explicit, voir : weblogs.asp.net/kennykerr/archive/2004/08/31/

1 votes

L'opérateur deux points : que vous avez utilisé pour invoquer le constructeur de la superclasse avant d'instancier le constructeur de la classe enfant, je suppose que c'est aussi vrai pour les méthodes ?

3 votes

@hagubear, seulement valable pour les constructeurs, AFAIK

288voto

puetzk Points 5099

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

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

Si un élément exécuté à ce stade échoue, les bases/membres qui avaient précédemment terminé la construction voient leurs destructeurs appelés et l'exception est renvoyée à l'appelant. Si vous voulez attraper les exceptions pendant le chaînage, vous devez utiliser un bloc try de fonction :

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 try es le corps de la fonction, plutôt que d'être à l'intérieur du corps de la fonction ; cela lui permet de capturer les exceptions lancées par les initialisations implicites ou explicites des membres et des classes de base, ainsi que pendant le corps de la fonction. Toutefois, si un bloc de capture de fonction ne lève pas une exception différente, le runtime relancera l'erreur originale ; les exceptions pendant l'initialisation ne peut pas être ignorée.

1 votes

Je ne suis pas sûr de comprendre la syntaxe de votre deuxième exemple... La construction try/catch remplace-t-elle le corps du constructeur ?

2 votes

Oui. J'ai reformulé la section, et corrigé une erreur (le mot-clé try va avant la liste d'initialisation). J'aurais dû vérifier au lieu d'écrire de mémoire, ce n'est pas quelque chose qui est utilisé souvent :-)

130 votes

Merci d'avoir inclus la syntaxe try/catch pour les initialisateurs. J'utilise le C++ depuis 10 ans, et c'est la première fois que je vois cela.

63voto

Dima Points 19888

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

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. L'initialisation des membres de données dans la liste d'initialisation est d'ailleurs préférable à l'affectation des valeurs de 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 des données sont toujours initialisés dans l'ordre dans lequel ils sont déclarés dans la définition de la classe, indépendamment de leur ordre dans la liste d'initialisation. Pour éviter des 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 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.

1 votes

Attendez une seconde... Tu dis que les initialisateurs économisent le coût des assignations. Mais les mêmes affectations n'ont-elles pas lieu à l'intérieur d'eux s'ils sont appelés ?

6 votes

Non. Init et affectation sont des choses différentes. Quand un constructeur est appelé, il essaiera d'initialiser chaque membre de données avec ce qu'il pense être la valeur par défaut. Dans la liste init, vous pouvez fournir des valeurs par défaut. Vous avez donc un coût d'initialisation dans les deux cas.

1 votes

Et si vous utilisez l'affectation à l'intérieur du corps, vous subissez de toute façon le coût d'initialisation, puis le coût de l'affectation en plus.

24voto

Nils Pipenbrinck Points 41006

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

En C++, vous ne pouvez pas construire une classe dérivée sans appeler le constructeur des parents. Soit cela se produit automatiquement si c'est un C'tor sans argument, soit cela se produit si vous appelez le constructeur dérivé directement comme indiqué ci-dessus ou votre code ne compilera pas.

22voto

CR. Points 96

La seule façon de transmettre des valeurs à un constructeur parent est de passer par une liste d'initialisation. La liste d'initialisation est implémentée avec un : et ensuite une liste de classes et les valeurs à passer au constructeur de cette classe.

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

Rappelez-vous également 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.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X