Constructeur

Fermé
MonCplusplus Messages postés 21 Date d'inscription lundi 23 avril 2018 Statut Membre Dernière intervention 9 mars 2019 - 27 déc. 2018 à 05:14
Dalfab Messages postés 706 Date d'inscription dimanche 7 février 2016 Statut Membre Dernière intervention 2 novembre 2023 - 31 déc. 2018 à 04:09
Bonjour, à tous voilà je suis en pleine quête d'apprentissage sur la programmation orienté objet en c++, d'après les livres et tutos que j'ai suivis on Suggère assez souvent d'initialiser les Attributs grâce à la liste d'initialisation.

Exemple : On suppose une classe Armoire ayant 2 attributs (largeur et hauteur).

Constructeur :
Armoire(int l=1,int h=1):largeur(l),hauteur(h){
cout << "Construction d'armoire" << endl;
} 

Comme on peut le remarquer pour une pierre de coup, J'ai redéfini le constructeur par défaut, et j'ai créé un constructeur prenant 2 paramètres. Cependant Je ne vois pas vraiment l'intérêt de la liste d'initialisation mis à part le fait qu' il initialise les Attributs avant de crée l'objet. Et Pour moi C'est très dangereux de procéder ainsi puisque il n'y aucune vérification effectué sur les valeurs entrées(paramètres) .
#include "Armoire.h"
int main() {
Armoire a1; // OK appel le constructeur par défaut initialise 1,1
Armoire a2(3,2)// OK appel le constructeur prenant les 2 paramètres et initialise 3,2
Armoire a3(-1,0)//Erreur Une Armoire ne peut pas avoir de dimension négative 
} 
En gros quel est l'intérêt d'utiliser la liste d'initialisation sachant qu'on devra dans tout les cas vérifiés les valeurs saisis par conséquent sa ne sert à rien selon moi, Quelqu'un peut-il m'éclairer à ce sujet Merci D'avance pour vos remarques !.

1 réponse

Dalfab Messages postés 706 Date d'inscription dimanche 7 février 2016 Statut Membre Dernière intervention 2 novembre 2023 101
Modifié le 27 déc. 2018 à 07:59
Bonjour,

Sur des variables de type simple, l'intérêt est mineur voire nul.
Pour ce qui est de vérifier la validité, on peut le faire après. S'il y a un problème seule chose à faire c'est déclencher une exception et l'objet ne sera pas créé.

Sur des types complexe, l'intérêt se voit plus :
Armoire( std::string nm ) : nom(nm) {}
Armoire( std::string nm ) {
    nom = nm;  // finalement la chaîne vaut nm
}
Dans le second cas, l'objet nom est créé comme étant une chaîne vide, puis on modifie la chaîne. Le premier qui crée une chaîne initialisée est plus optimal surtout si le type du champ à construire nécessite un code important.

Si maintenant, on suppose qu'un des champs n'est pas constructible par défaut. La liste d'initialisation est alors le seul moyen de créer ce champ, elle devient incontournable
0
MonCplusplus Messages postés 21 Date d'inscription lundi 23 avril 2018 Statut Membre Dernière intervention 9 mars 2019
30 déc. 2018 à 07:07
Bonjour , Merci pour l’éclaircissement et tes remarques assez intéressantes. Mais justement là où c'est confus c'est comme tu le dis on peut vérifié la validité après donc j'entends par là le faire de cette manière
Armoire(int l=1,int h=1):largeur(l),hauteur(h){
while(largeur<1 || hauteur<1){
cout << "Dimension incorrecte ( dim < 1)" << endl;
cout << "Largeur ? ";
cin >> largeur;
cout << "hauteur ? ";
cin >> hauteur;
} 
}


Mais on est d'accord que même en supposant des valeurs incorrectes la liste d'initialisation est inutile , on aurait très bien pu sans passer ? Sachant que dans tout les cas on vérifiera les valeurs des attributs. Pour ce qui est des types complexe(objets) l'issu est assez similaire non ?

Armoire( std::string nm ) : nom(nm) {}
#include "Armoire.h"
#include <iostream>
using namespace std;
int main(){
Armoire a1 (" ") // On crée également une Armoire avec une chaîne vide 
return 0;
}


Certes , une il y'a différence considérable et non négligeable en ayant recours a la liste d'initialisation mais ce que je veux dire c'est que sa ne garanti pas toujours une intégrité sur les valeurs saisies. Désolé si je suis borné mais sa m’intéresse vraiment de pouvoir peser le pour et le contre concernant certaines subtilité en programmation. Merci d'avance !
0
Dalfab Messages postés 706 Date d'inscription dimanche 7 février 2016 Statut Membre Dernière intervention 2 novembre 2023 101
31 déc. 2018 à 04:09
Ton premier exemple est non naturel. On suppose qu'en entrée on a des valeurs et que le constructeur peut en changer spontanément. Il est plus sain d'avoir : soit le constructeur gère en interne ses valeurs initiales, soit elles sont fournies et il pourra les accepter ou les refuser.

On est d'accord, dans le cas de variables simple on peut se passer de la liste d'initialisation.

Pour les types complexes, non on ne peut pas s'en passer pour les 2 raisons que j'ai déjà indiqué.
Et ton 2nd exemple, on a les deux possibilités :
Armoire( std::string nm ) : nom(nm) {
   if ( nom == "toto") throw "erreur la chaîne \"toto\" n'est pas autorisée";
}
Armoire( std::string nm ) {
   if ( nm == "toto") throw "erreur la chaîne \"toto\" n'est pas autorisée";
    nom = nm;  // finalement la chaîne vaut nm
}
En reprenant le code du constructeur sans liste d'initialisation, on aura 3 étapes :
- création de l'objet avec le constructeur par défaut d'une chaîne qui crée une chaîne vide (appel de
std::string::string()
)
- stopper la création si la création n'a pas de sens
- mise à jour de la chaîne en lui affectant une chaîne vide (appel de
std::string::operator=( const char *prm)
avec
prm=""
)
Avec la liste d'initialisation, il n'y a que deux étapes :
- création de l'objet avec le constructeur
std::string::string(const char*prm)
avec
prm = ""
)
- stopper la création si la création n'a pas de sens.
Mais supposer que cela se passe comme ton premier exemple où il y a des prises de décisions en amont et en interne au constructeur, c'est cela ne serait ni sain ni optimum sans liste d'initialisation.
0