• Aucun résultat trouvé

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(){....}