8. CONSTRUCTEUR ET DESTRUCTEUR
8.2 CONSTRUCTEUR .1 Constructeur implicite
8.2.4 Transtypage par appel d'un constructeur
8.2.4 Transtypage par appel d'un constructeur
En langage C++, deux opérations de transtypages explicites sont définies : la forme traditionnelle du langage C et le transtypage fonctionnel du langage C++.
Synopsis du transtypage fonctionnel
Un nom de type suivi d'une expression entre parenthèses convertit l’expression conformément au type de retour spécifié par l’appel du constructeur implicite associé.
Quand expression est une liste, le transtypage s'effectue par l'appel d’un constructeur, l'objet devant être membre d'une classe dotée du constructeur adéquat.
Exemple 1 float f;
long i = (long) f; // forme traditionnelle
i = long(f); // forme fonctionnelle par appel du constructeur implicite d’une instance // de la classe long
Exemple 2
// Transtypage fonctionnel par appel explicite du constructeur par défaut de la classe int
#include <iostream.h>
int main()
{int i = int(1.2); cout << " i = " << i << endl ; i = int(); cout << " i = " << i << endl ; int j = int(); cout << " j = " << j <<endl;
return(1);
}
// résultat i = 1 i = 0 j = 0
Conversions implicites
Les conversions implicites sont exécutées dès qu'existe un constructeur dont le premier argument est du même type que l'objet source. Dans l'exemple ci-dessous, le nombre entier situé à la droite de l'affectation est convertie en un objet de la classe Entier.
Exemple Soit la classe Entier
#include <iostream.h>
class Entier {public :
int i;
Entier(int j) {i=j;} // constructeur de transtypage des nombres entiers };
int main() {int j=2;
Entier e1(j);
Entier e2=j;
Entier e3(5) ;
cout << " e1 = " << e1.i << " e2 = " << e2.i << " e3 = " << e3.i << endl;
}
Le mot clé explicit
Le mot-clé explicit utilisé avant la déclaration du constructeur force la conversion explicite à partir d'un transtypage fonctionnel.
Exemple
#include <iostream.h>
class Entier {public : int i;
public:
explicit Entier(int j) {i=j; return ;}
};
// la conversion d'un entier en objet de classe Entier nécessite un transtypage explicite int main()
{ int j=6;
// Entier e1; // ereur
Entier e1=(Entier) j; // e1.i=6 Entier e2=Entier(j); // e2.i=6 Entier e3=Entier(5); // e3.i=5 cout << " e1.i = " << e1.i << endl;
cout << " e2.i = " << e2.i << endl;
cout << " e3.i = " << e3.i << endl;
}
Remarque
Le type d'un objet ne peut pas être composé dans la forme fonctionnelle, la restriction étant syntaxique, non sémantique.
Exemple
#include <iostream.h>
int main()
{char c = 'a', * p_c = &c;
typedef int* p_int;
p_int p_i;
p_i = p_int(p_c); // OK // p_i = int*(p_c); // KO
cout << " c = " << c << " *p_c = " << *p_c << " *p_i = " << *p_i << endl;
}
// résultat
c = a *p_c = a *p_i = 97
Exercice 1
1°) Créer une classe de nombres complexes avec un constructeur devant prendre en compte toutes les situations possibles d'initialisation de ses instances.
2°) Instancier quatre objets, dans des situations d'initialisation différentes, de telle sorte que ce constructeur soit appelé avec un transtypage fonctionnel. On constatera que la situation peut être ambigüe.
#include <iostream.h>
class complexe {public:
float reel; float imaginaire;
complexe(double x=0, double y=0){reel = x; imaginaire =y;} // constructeur inline };
int main() // initialisation avec appel du constructeur et transtypage fonctionnel
{// complexe z0 = {1,10}; // initialisation traditionnelle interdite si constructeur défini complexe z1(1.5, -10.78); // 2 flottants
complexe z2(-1,3); // 2 entiers et transtypage fonctionnel, identique à // complexe z2= complexe(-1,3);
complexe z3 = complexe(); // valeur par défaut, mais // complexe z3(); est interdit complexe z4 = complexe(-5); // un seul argument identique à
// complexe z4(-5, 0);
complexe z5(0,-5);
cout << "z1.reel = " << z1.reel << "\tz1.imaginaire = " << z1.imaginaire << endl ; cout << "z1.reel = " << z1.reel << "\tz1.imaginaire = " << z1.imaginaire << endl ; cout << "z2.reel = " << z2.reel << "\tz2.imaginaire = " << z2.imaginaire << endl ; cout << "z3.reel = " << z3.reel << "\tz3.imaginaire = " << z3.imaginaire << endl ; cout << "z4.reel = " << z4.reel << "\tz4.imaginaire = " << z4.imaginaire << endl ; cout << "z5.reel = " << z5.reel << "\tz5.imaginaire = " << z5.imaginaire << endl ; }
// résultat
z1.reel = 1.5 z1.imaginaire = -10.78 z2.reel = -1 z2.imaginaire = 3
z3.reel = 0 z3.imaginaire = 0 z4.reel = -5 z4.imaginaire = 0 z5.reel = 0 z4.imaginaire = -5
Exercice 2
Ecrire une deuxième version du même exercice avec une méthode de saisie et une méthode d'affichage, publiques, permettant d'accéder sous contrôle aux parties réelles et imaginaires, privées.
#include <iostream.h>
class complexe
{float reel; float imaginaire;
public:
complexe(double x=0, double y=0){reel = x; imaginaire =y;} // constructeur inline void affiche(char *);
void affiche(void);
void saisie(char *);
};
void complexe::affiche(char * chaine)
{cout << "Nombre complexe " << chaine << endl;
cout << "partie reelle :" << this->reel << " partie imaginaire : " << this->imaginaire << endl;
}
void complexe::saisie(char * chaine)
{cout << "Nombre complexe " << chaine << endl;
cout << "saisir la partie reelle :" ; cin >> this->reel;
cout << endl << "saisir la partie imaginaire : ";
cin >> this->imaginaire;
}
int main() // initialisation avec appel du constructeur et transtypage fonctionnel
{// complexe z0 = {1,10}; // initialisation traditionnelle interdite si constructeur défini void complexe::affiche(char *);
void complexe::saisie(char *);
complexe z1(1.5, -10.78); // 2 flottants
complexe z2(-1,3); // 2 entiers et transtypage fonctionnel, identique à // complexe z2= complexe(-1,3);
complexe z3 = complexe(); // valeur par défaut, mais complexe z3(); interdit complexe z4 = complexe(-5); // un seul argument identique à complexe z4(-5, 0);
z1.affiche("z1");
z2.affiche("z2");
z3.affiche("z3");
z4.affiche("z4");
z5.saisie("z5");
z5.affiche("z5");
}
Exercice 3
1°) Créer une classe de nombres complexes avec deux constructeurs. Il faudra redéfinir le constructeur par défaut pour éviter l'ambiguïté de l'exemple précédent.
2°) Instancier cinq objets de telle sorte que ces constructeurs soient appelés suite à un transtypage fonctionnel dans cinq situations d'initialisation différentes de telle sorte qu'une situation ambiguë soit impossible.
#include <iostream.h>
class complexe {public:
float reel; float imaginaire;
//constructeurs
complexe() {reel =0; imaginaire = 0;} // constructeur par défaut
complexe(double x, // x = 0 impossible car ambiguité avec le constructeur par défaut double y=0 /* nécessaire pour z4 */)
{reel = x; imaginaire =y;}
};
int main()
{// initialisation avec appel des constructeurs et transtypage fonctionnel complexe z1(1,10), z2= complexe(-1,3), z3 = complexe(), z4(-5) , z5(0,-5);
cout << "z1.reel = " << z1.reel << "\tz1.imaginaire = " << z1.imaginaire << endl ; cout << "z2.reel = " << z2.reel << "\tz3.imaginaire = " << z2.imaginaire << endl ; cout << "z3.reel = " << z3.reel << "\tz3.imaginaire = " << z3.imaginaire << endl ; cout << "z4.reel = " << z4.reel << "\tz4.imaginaire = " << z4.imaginaire << endl ; cout << "z5.reel = " << z5.reel << "\tz5.imaginaire = " << z5.imaginaire << endl ; return(1);
}
// résultat
z1.reel = 1 z1.imaginaire = 10 z2.reel = -1 z2.imaginaire = 3 z3.reel = 0 z3.imaginaire = 0 z4.reel = -5 z4.imaginaire = 0 z5.reel = 0 z5.imaginaire = -5
8.3 DESTRUCTEUR
Destructeurs implicite et explicite
Le destructeur est une méthode appelée quand une instance sort de la portée de la classe.
Le destructeur par défaut libère l'espace mémoire de chacune des données membres de l'instance.
Un destructeur ne peut être surchargé, son contexte d'utilisation étant inconnu. Il est donc unique.
L'unique destructeur explicite, sans argument, d'une classe C est défini à partir de l'opérateur ~ selon la syntaxe :
[C::]~C(){....}