Les constructeurs/destructeurs
Principe de la construction/destruction.
Un objet est une variable de type d'une classe qui a ete definit precedement. J'avais utilise dans le texte precedent le terme de "créé" lors de la déclaration de l'objet, c'est parceque il est lpus compliquer d'intialiser un objet qu'une variable de type primaire. Il faut par exemple reserver de la memoire, initialiser les variables et eventuellement afficher des commentaire, une fenetre... Pour faire tout cela on va utilise une fonction du nom de constructeur. On aura egalement besoin de libérer la memoire et plus eventuellement. Pour faire cela on utilisera un destucteur.Regle de definition des constructeur/destructeur
Les constructeurs/destructeurs sont des fonctions qui doivent obéir à certaine règles:
- Elles ne doivent pas renvoyer de valeur (pas même void...).
- Elles doivent être sous l'étiquette public (évidement sinon on ne pourrait pas les appeler...)
- Le destructeur ne peut pas prendre d'arguments.
- Le nom du constructeur doit etre le même que le nom de la classe, et le destructeur doit s'apeller ~nomdelaclasse
Il y a deux constructeurs qui sont remarquables. Le premier est nommé constructeur par defaut, et le second constructeur par recopie. Le constructeur par défaut est un constructeur auquel on ne passe pas de paramètre(s). Il peut éventuellement prendre des paramètres par défaut ou ne pas prendre de paramètres. Le constructeur par recopie a pour prototype nomdelaclasse::nomdelaclasse(nomdelaclasse&);. Et oui, une référence d'un objet a copier. Si le constructeur est redefini, alors il faut impérativement que le constructeur par défaut le soit aussi.
On redéfinit plus rarement le constructeur par recopie, car il se contente de faire une copie champ a champ de l'objet. En outre, il arrive qu'il soit redéfini lorsqu'un objet a pour champ un pointeur sur un objet dynamique. A ce moment là, on peut soit vouloir faire un deuxieme pointeur vers ce même objet (auquel cas on ne redéfinira pas le constructeur par recopie), soit vouloir avoir un deuxième objet dynamique (il faudra donc le construire et pour cela redéfinir le constructeur par recopie).
Exemple commenté.
class chat
{
public:
float taille, poids;
char pellage[10];
chat(float t, float p, char *pel);
chat(){} /* Il faut toujours définir un constructeur par defaut.
On aurait egalement pu mettre des paramètres par defaut au
constructeur du dessus. */
~chat(void);
}
chat::chat (float t, float p, char *pel)
{
taille=t;
poids=p;
strcpy(pellage, pel);
cout << "Le chat nait donc un jour il moura...:))";
}
chat::~chat (void)
{
cout<<"Le chat est mort...";
}
int main (void)
{
chat tom(1.80,70,"brun"); // On passe les argument au moment de la création
return 0;
}
Le difficile problème de la construction par défaut.
Dans ce cas, comment créerons-nous un objet de type chat en utilisant le constructeur par defaut (sans argument)? Ferons-nous: chat tom(); ? En fait non, car selon la forme générale de la ligne ci-dessus, on déclare une fonction qui renvoie on objet de type chat ne prenant pas d'argument. Nous ferons donc: chat tom;.
J'ai dit plus haut qu'il fallait impérativement redéfinir un constructeur par défaut lorsque l'on surcharge le constructeur. La reponse à cela est tres simple. Elle est en rapport avec les tableaux statiques d'objets. Si on fait un tableau d'objets, il faut qu'il soit construit, et comme on ne peut pas leur passer de paramètres, c'est le constructeur par defaut qui sera appelé. Il serait quand même dommage de se priver de tableaux parceque l'on ne veut pas redéfinir une fonction qui, la plupart du temps, est vide...
Liste d'initialisation.
Lors de la redéfinition du constructeur avec argument de chat, étudions ce qui se passe. C++ crée les variables et ensuite les initialise. Il semble évident que ce n'est pas optimal, et qu'il serait bien mieux d'initialiser les variables au moment de leur creation.
C'est ici qu'interviennent les listes d'initialisation. En effet il est possible d'initialiser dans les objet les variables au moment de leur creation. "Quand allons-nous faire ca? ", allez vous dire. Nous allons agir avant le corps du constructeur. Il parait difficile d'expliquer la syntaxe, il me semble qu'un exemple sera plus parlant:
// Il est impossible d'initialiser pelage car c'est une chaine de caractere
chat::chat (float t, float p, char *pel):taille(t), poids(p)
{
strcpy(pellage, pel);
cout<<"Le chat nait donc un jour il mourra...:))";
}