Les objets automatiques sont crée par une déclaration située :
- Dans une fonction : Il sera créé lorsque le programme rencontrera la déclaration et détruit à la fin de la fonction. La déclaration peut se situer après des instruction exécutable.
- Dans un bloc : il sera crée de la même manière que pour une fonction, et détruit a la fin du bloc.
Les objets statiques sont crée par une déclaration situé :
- En dehors de toutes fonctions.
- Dans une fonction précédée du qualificatif static.
Les objets statiques sont crée avant le début de l'exécution de la fonction main et détruit après la fin de son exécution.
Si un objet comporte un constructeur, il doit être déclaré avec se constructeur en lui fournissant le nombre d'arguments nécessaires a celui-ci. Si il existe plusieurs constructeur, la déclaration devra contenir le nombre d'argument voulu par un de ces constructeur.
En ce qui concerne la chronologie :
- Le constructeur est appelé après la création de l'objets.
- Le destructeur est appelé avant la destruction de l'objet.
Rappel : Lorsqu'il n'existe pas de constructeur ou qu'il existe un seul constructeur sans argument la déclaration est du type :
point a; // attention, point a() est une erreur
Supposons la déclaration de la classe suivante :
class point { int x,y; public: point(int,int); void deplace(int,int); void affiche(); };
et si nous déclarons :
point *adr;
alors nous disposons d'un pointeur sur un objet de la classe point. Nous pouvons affecter la valeur d'un objet de la classe par l'instruction :
adr = new point(7,6);
Nous accédons aux fonctions membre de la classe par les instructions suivante :
adr->affiche(); adr->deplace(-5,6);
Ou bien encore par :
(*adr).affiche(); (*adr).deplace(-5,6);
Quant à la suppression de l'objet elle se fera par :
delete adr;
L'appel du constructeur se fait automatiquement par l'opérateur new, ainsi si des arguments son nécessaire à la création de l'objet ils doivent lui être passé. De même le destructeur sera appelé par l'opérateur delete.
Nous venons de voir les cas possibles de création normale d'un objet, mais il peut arriver qu'il y ait création d'un objet sans demande explicite du programmeur. Lorsque par exemple la valeur de retour d'une fonction est un objet ou encore lorsque l'on passe un objet en arguments d'une fonction.
- Le constructeur par recopie :
Le cas le plus simple est lorsque le programmeur décide de créé un nouvel objet à partir d'un objet déjà créé. En effet il existe un constructeur par défaut pour ce genre de création d'objets. Mais cette recopie est "superficielle", donc si l'objet comporte des pointeurs ils ne seront que recopiés et non pas recréé.
point a(1,7); point b=a;
Bien sur nous avons la possibilité de créer notre propre constructeur de recopie afin de pallier le problème des pointeurs. Ce constructeur ne doit prendre qu'un seul argument en paramètre du type de la classe de l'objet. Cet objet sera obligatoirement transmit par référence(sinon il y aura appel du constructeur de recopie par défaut). Il peut être assortie du qualificatif const afin d'être sur que l'objet transmit ne sera pas modifié(fortement conseillé !!). L'en-tête du constructeur sera du type :
point(point&); ou point(const point&); point(point); // erreur à la compilation!!
Maintenant c'est au programmeur de prendre tout la recopie en charge qu'elle soit "superficielle" ou "profonde"(pour les pointeurs).
La construction de recopie est différente de l'affectation que nous avons déjà rencontrais, en effet dans l'affectation les deux objets existe déjà. Pour une affectation en "profondeur" on utilise la surdéfinition de l'opérateur "=".
- Exemple d'utilisation: Objet transmis par valeur :
Nous allons ici utiliser une classe vect qui comprends une donné nb_elm et un adr pointeur sur les éléments du "vecteur". Ainsi qu'un constructeur et un destructeur :
class vect { int nb_elm; double *adr; public: vect(int nbe){double adr= new double[nb_elm=nbe];} ~vect(){delete adr;} };
Si nous décidons de transmettre à une fonction un objet de la classe vect il y aura création d'un deuxième objet de la même classe de recopie. Si nous n'avons pas définit de constructeur par recopie le deuxième objet crée possédera un pointeur désignant la même adresse mémoire que le premier :
void fonct(vect b); void main(void) { vect a(10); fonct(a); }
Lors du passage en argument de a, un objet b est crée par recopie de l'objet a. Comme il n'existe pas de constructeur par recopie définit par l'utilisateur, c'est celui par défaut qui est utilisé, et c'est donc une construction "superficielle" qui a lieu. Les pointeurs adr de a et de b pointe alors sur la même adresse mémoire. Lors de la sortie de la fonction fonct le destructeur sera appelé pour l'objet b puis à la fin du main le destructeur sera appelé pour a, mais l'adresse mémoire n'existent déjà plus, cela peut poser des erreurs d'exécution surtout si on utilise a par la suite.
Pour remédier à ce problème nous devons utiliser un constructeur par recopie. Qui lorsqu'il sera appelé devra :
- Créer dynamiquement un nouvel emplacement dans lequel il recopie les valeurs de l'objet transmit en argument.
- renseigner convenablement les données du nouvel objet, nb_elm avec le nombre de valeur et adr avec le nouvel emplacement mémoire
Voici le code de ce nouveau constructeur :
vect(const vect &v) { adr =new double[nb_elm=v.nb_elm]; //création du nouvel objet for (int i=0;inb_elm;i++) { adr[i]=v.adr[i]; //recopie de l'ancien } }
Vous constatez maintenant que chaques objets a sa place mémoire et que lors de la destruction de b plus aucun problème de mémoire ne se posent.
- Objet transmit en valeur de retour d'une fonction :
De même que pour la transmissions d'arguments, la valeur de retour d'une fonction peut être un objet et c'est donc le constructeur par recopie qui entre en jeu. S'il n'existe pas explicitement, c'est le constructeur de recopie par défaut qui est utilisé, et si l'objet comporte une partie dynamique, c'est une recopie superficielle qui est opéré. Mais comme adr pointe sur un emplacement mémoire cela peut engendrer des problèmes d'exécution si l'emplacement mémoire est réutilisé.