web-dev-qa-db-fra.com

C++ appelant des constructeurs de classe de base

#include <iostream>
#include <stdio.h> 
using namespace std;

// Base class
class Shape 
{
   public:
      void setWidth(int w)
      {
         width = w;
      }
      void setHeight(int h)
      {
         height = h;
      }
      Shape()
      {
    printf("creating shape \n");
      }
      Shape(int h,int w)
      {
     height = h;
         width = w;
         printf("creatig shape with attributes\n");
      } 
   protected:
      int width;
      int height;
};

// Derived class
class Rectangle: public Shape
{
   public:
      int getArea()
      { 
         return (width * height); 
      }
      Rectangle()
      {
     printf("creating rectangle \n");
      }
      Rectangle(int h,int w)
      {
     printf("creating rectangle with attributes \n");
     height = h;
         width = w;
      }
};

int main(void)
{
   Rectangle Rect;

   Rect.setWidth(5);
   Rect.setHeight(7);

   Rectangle *square = new Rectangle(5,5);
   // Print the area of the object.
   cout << "Total area: " << Rect.getArea() << endl;

   return 0;
}

Le résultat du programme est donné ci-dessous

creating shape 
creating rectangle 
creating shape 
creating rectangle with attributes 
Total area: 35

Lors de la construction des deux objets de classe dérivés, je constate que c'est toujours par défaut le constructeur de la classe de base appelé en premier. Y a-t-il une raison à cela? Est-ce la raison pour laquelle les langages tels que python insistent sur les appels explicites des constructeurs de classe de base plutôt que sur les appels implicites tels que C++?

26
liv2hak

La réponse courte à cela est "parce que c'est ce que la norme C++ spécifie".

Notez que vous pouvez toujours spécifier un constructeur différent de celui par défaut, comme suit:

class Shape  {

  Shape()  {...} //default constructor
  Shape(int h, int w) {....} //some custom constructor


};

class Rectangle : public Shape {
  Rectangle(int h, int w) : Shape(h, w) {...} //you can specify which base class constructor to call

}

Le constructeur par défaut de la classe de base est appelé uniquement si vous ne spécifiez pas celui à appeler.

59
maditya

Le constructeur de classe par défaut est appelé sauf si vous appelez explicitement un autre constructeur de la classe dérivée. la langue le spécifie. 

Rectangle(int h,int w):
   Shape(h,w)
  {...}

Appellera l'autre constructeur de la classe de base. 

12
rerun

Lorsque les objets sont construits, il s'agit toujours d'abord d'un sous-objet de classe de base de construction. Par conséquent, le constructeur de classe de base est appelé en premier, puis les constructeurs d'appels de classe dérivés. La raison en est que les objets de classe dérivés contiennent des sous-objets hérités de la classe de base. Vous devez toujours appeler le constructeur de la classe de base pour initialze les sous-objets de la classe de base. Nous appelons généralement le constructeur de la classe de base sur la liste d'initialisation des membres de la classe dérivée. Si vous n'appelez pas explicitement le constructeur de la classe de base, la compilation appellera le constructeur par défaut de la classe de base pour initialiser le sous-objet de la classe de base. Cependant, l'appel implicite sur le constructeur par défaut ne fonctionne pas nécessairement à tout moment (par exemple, si la classe de base définit un constructeur qui ne pourrait pas être appelé sans arguments).

Lorsque les objets sont hors de portée, le programme appelle d'abord le destructeur de la classe dérivée, puis le destructeur de la classe de base.

4
taocp

Dans c ++, le compilateur s'assure toujours que les fonctions de la hiérarchie d'objets sont appelées avec succès. Ces fonctions sont des constructeurs et des destructeurs et la hiérarchie des objets signifie l'arbre d'héritage. 

Selon cette règle, nous pouvons supposer que le compilateur appellera des constructeurs et des destructeurs pour chaque objet de la hiérarchie d'héritage, même si nous ne l'implémentons pas. Pour effectuer cette opération, le compilateur synthétisera les constructeurs et les destructeurs non définis et les nommera constructeurs et destructeurs par défaut. Ensuite, le compilateur appellera le constructeur par défaut de la classe de base, puis le constructeur de la classe dérivée.

Dans votre cas, vous n'appelez pas le constructeur de la classe de base, mais le compilateur le fait pour vous en appelant le constructeur par défaut de la classe de base, car si le compilateur ne le faisait pas, votre classe dérivée, Rectangle dans votre exemple, ne serait pas complète et pourrait provoquer un sinistre car vous utiliserez peut-être une fonction membre de la classe de base dans votre classe dérivée. Donc, pour des raisons de sécurité, le compilateur a toujours besoin de tous les appels de constructeur. 

1
Validus Oculus

Imaginez cela comme ceci: lorsque votre sous-classe hérite des propriétés d'une super-classe, elles n'apparaissent pas comme par magie. Vous devez toujours construire l'objet. Donc, vous appelez le constructeur de base. Imaginez que votre classe hérite d'une variable, que votre constructeur de super-classe initialise à une valeur importante. Si nous ne le faisions pas, votre code pourrait échouer car la variable n'était pas initialisée.

0
Thomas Hobohm

Pourquoi le constructeur par défaut de la classe de base est appelé? Il s'avère que ce n'est pas toujours le cas. Tout constructeur de la classe de base (avec des signatures différentes) peut être appelé à partir du constructeur de la classe dérivée. Dans votre cas, le constructeur par défaut est appelé car il n’a pas de paramètres, il est donc par défaut.

Lorsqu'une classe dérivée est créée, l'ordre dans lequel les constructeurs sont appelés est toujours Base -> Dérivé dans la hiérarchie. Si nous avons:

class A {..}
class B : A {...}
class C : B {...}
C c;

Lorsque c est créé, le constructeur de A est appelé en premier, puis le constructeur de B, puis le constructeur de C.

Pour garantir cet ordre, lorsque le constructeur d'une classe dérivée est appelé, il appelle toujours le constructeur de la classe de base avant que le constructeur de la classe dérivée ne puisse faire autre chose. Pour cette raison, le programmeur peut appeler manuellement un constructeur de la classe de base dans la seule liste d'initialisation du constructeur de la classe dérivée, avec les paramètres correspondants. Par exemple, dans le code suivant, le constructeur par défaut de Derived appellera le constructeur de Base, Base :: Base (int i), au lieu du constructeur par défaut.

Derived() : Base(5)
{      
}

Si aucun constructeur de ce type n'est appelé dans la liste d'initialisation du constructeur de la classe dérivée, le programme suppose un constructeur de la classe de base sans paramètre. C'est la raison pour laquelle un constructeur sans paramètre (c'est-à-dire le constructeur par défaut) est appelé.

0
Tris