Il y a déjà beaucoup de sujets de personnes demandant l'intérêt de la liste d'initialisation. Ce n'est pas mon cas je l'ai bien compris.
Vu que on se fait taper sur les doigts par nos cher gourou du c++ quand on ne l'utilise pas ma question est quelle est la bonne facon de faire quand on a quelque chose comme ca
Nos setters sont pour la plupart du temps la pour une bonne raison du coup j'ai toujours envie de les utiliser directement dans le constructeur non ? Mais du coup ben la moitié des variables chez moi ne passeraient pas par la liste d'initialisation du coup. J'ai loupé quelque chose ?
HS : Il n'y a pas de définition formelle (je crois) à ce qu'est un accesseur, mais pour moi, c'est un simple accès à une variable membre, avec ou sans test des conditions et invariants de classes. Typiquement, cela peut dire que l'on peut écrire :
HS2 : est ce que c'est le boulot de setNb de vérifier la valeur ? Est-ce que ça ne serait pas plutôt une erreur de programmation, si la valeur invalide arrive jusque là ? (elle devrait plutôt être vérifiée lors de la saisie ou de la lecture d'un fichier par exemple).
HS3 : les accesseurs sont généralement une violation de Demeter, dû à une mauvaise compréhension de l'encapsulation.
Pour revenir à la question, oui, bien sûr que si on doit appeler une fonction membre, on devra le faire dans le corps du constructeur et pas dans la liste d'initialisation.
Merci beaucoup de ton intervention gbdivers (même tes HS m'ont fait comprendre des choses et ne sont donc pas si HS que ca )
Pour le HS2 : c'est un cas inventé bidon pour l'exemple mais j'aurais tendance à te dire que pour moi oui puisqu'on m'avait toujours appris que le boulot du setter etait de justement éviter d'assigner n'importe quoi à une variable en faisant ce genre de controles et que du coup le assert de ton exemple ne passe pas toujours. Ca me perturbe un peu puisque du coup je ne vois finalement plus leur intéret puisque dans ce cas j'ai limpression que mettre la variable public et ne plus avoir d'encapsulation sur celle-ci reviendrait au même.
Donc finalement pour toi si l'on en vient à vouloir utiliser un setter de ce genre dans un constructeur c'est plutôt une erreur de conception dans le programme.
Tu me perturbes mais ca m'intéresse grandement ^^.
Soit votre classe est un simple stockage de données et généralement, il est plus simple d'utiliser directement les champs.
Soit votre classe offre des services et l'utilisateur de cette classe doit se conformer à une API qui ne donne aucune indication sur comment la classe implémente son service. Il est donc alors peu probable que la connaissance de champs internes de la classe soit une bonne idée.
Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.
Prenons un exemple concret : un véhicule (pour faire plaisir à notre koala préféré )
Tu vas peut être avoir un membre "réservoir d'essence", qui contient la quantité d'essence dans le réservoir.
Pour lorsque tu vas mettre de l'essence dans le réservoir, cela ne va pas simplement changer la variable membre, mais également changer le poids du véhicule, casser le moteur si tu mets la mauvaise essence, permettre de rouler plus vite si tu mets de l'essence avec additifs, etc. C'est la différence fondamentale entre "modifier une variable membre" (setter) et rendre un service en conception objet.
Bien sur, si tu n'as pas toutes ces fonctionnalités à gérer dans ta classe, tu vas peut être au final te retrouver avec un code proche de ton "setNb", qui ne fait que vérifier que tu ne dépasses pas la capacité du réservoir et qui modifie simplement ta variable membre.
Il est probable que l'on préférera dans ce cas un fonction "addEssence" plutôt que "setEssence", pour éviter d'utiliser cette fonction à mauvais escient. Par exemple, on pourrait être tenté de calculer la distance parcouru par le véhicule, multiplier par la consommation kilométrique et faire un setEssence pour modifier la quantité d'essence. Et là, très clairement, on pense le véhicule en termes d'ensemble de variables et non en tant que services rendus.
Le problème au final n'est pas les setter/getter. Ce sont des fonctions qui réduisent la qualité du code (couplage plus fort entre interface et implémentation des classes), mais cela peut être un choix volontaire de la part du développeur. Le problème est surtout la "mauvaise" façon de concevoir ses classes, l'utilisation de accesseurs qui en découle, et le fait que faire cela par défaut, sans comprendre les conséquences sur la qualité du code.
Ton exemple m'a bien éclairé sur le conception en général sur les setters et autres fonctions de service
Pour conclure alors avant de passer en résolu, gardons notre voiture. Celle-ci à sa construction doit initialiser son réservoir d'essence, disons qu'il n'y a pas de trou noir dedans et donc qu'il ne peut être négatif
C'est quand même bien à mon constructeur de se défendre et de lever une exception ou dans mon cas plutôt préférer le laisser juste vide et ainsi devoir me manger une condition dans le corps de mon constructeur (et ne pas utiliser la liste) ?
Dans la logique, c'est selon ton avis. Si pour toi une voiture ne peut pas avoir un réservoir négatif et que si c'est le cas c'est pas bon, alors oui tu peux lancer une exception qui empêchera la construction d'un objet aux caractéristiques non valides. Si pour toi tu te dis "c'est l'utilisateur qui fait le crétin", tu pose une assertion dans le constructeur.
Une création de voiture avec réservoir négatif, c'est une erreur de programmation. Donc je pense qu'on devrait claquer une bonne vieille assertion, pas lancer une exception.
Mehdidou99 - Plus on apprend, et euh... plus on apprend. | Ne lisez pas le cours de C++ d'OpenClassrooms ! Il est de mauvaise qualité. Essayez plutôt celui-là. Jeux (3D) en C++ ? Unreal Engine 4, c'est bien, mangez-en !
Pour conclure alors avant de passer en résolu, gardons notre voiture. Celle-ci à sa construction doit initialiser son réservoir d'essence, disons qu'il n'y a pas de trou noir dedans et donc qu'il ne peut être négatif
C'est quand même bien à mon constructeur de se défendre et de lever une exception ou dans mon cas plutôt préférer le laisser juste vide et ainsi devoir me manger une condition dans le corps de mon constructeur (et ne pas utiliser la liste) ?
Donc tu considères que si le volume dans le réservoir d'essence n'est pas correct, la voiture ne doit pas être construite, il faut la détruire ? J'espère que tu ne bosses pas pour un fabriquant de voiture, tu vas leur coûter cher
Le rôle d'un constructeur est de mettre l'objet dans un état valide. Rien de plus. La construction d'un objet ne devrait échouer que si l'état interne n'est pas valide.
Pour une voiture, c'est quoi l'état valide ?
Dans un cas, ça sera valide après avoir monté la carrosserie. Le reste (monter les sièges, mettre le peinture sur la carrosserie, etc) sera considéré comme de la personnalisation, à la liberté du client.
A l'autre extrême, une voiture est "finie" quand elle est prête à partir en vacances, avec les valises dans le coffre, les enfants avec leur ceinture de sécurité et l'auto-radio allumé.
Si tu fais un jeu de gestion de garage automobile, tu auras un voiture avec un constructeur qui te permet pas de remplir le réservoir (puisqu'il n'y aura pas de réservoir à ce moment là). Si tu fais un jeu de gestion de voyage, ton constructeur te donnera un voiture prête à partir, réservoir plein.
Il n'y a pas de limite haute à ce que tu peux faire dans le constructeur (il y a juste une limite basse = avoir un état valide). C'est une question de choix de design. Plus tu mettras de fonctionnalités dans le constructeur, plus il sera facile de construire un objet en une seule étape, mais plus ton interface sera lourde. Et la construction de ton objet risque d'échouer pour un paramètre accessoire.
Tu vas donc avoir 2 solutions pour remplir ton réservoir : dans le constructeur ou à côté. Cela donne cela :
vehicule vroumvroum(40); // remplit le réservoir avec 40L
// ou
vehicule supervroumvroum;
supervroumvroum.addEssence(40);
La première version permet de gagner une ligne, mais est-ce réellement important ?
Par contre, il est clair que dans les 2 cas, remplir le réservoir passera par la fonction addEssence. On évite d'écrire 2 fois le même code, donc on ne va pas écrire le code de vérification de la quantité d'essence dans le constructeur et dans addEssence. Donc le constructeur appellera addEssence.
Bref, dans les 2 cas, tu ne peux pas initialiser le réservoir dans la liste d'initialisation et tu dois passer pas addEssence. Mais la question importante est : est-ce que tu dois proposer un constructeur qui permet de remplir le réservoir ?
Merci à tous et merci beaucoup gbdivers tu as mis le doigt finalement sur ma question qui était en fait sur le bout de ma langue : c'est quoi finalement la philosophie d'un constructeur. Ton dernier post est la réponse absolue. A priori on peut maintenant juste voter et pas mettre en avant la réponse qui a résolu comme y a un moment ? ou je suis aveugle.
A priori on peut maintenant juste voter et pas mettre en avant la réponse qui a résolu comme y a un moment ?
Peut importe. C'est une réponse qui a déjà été donné et qui sera encore donné. Personne ne lis les anciennes discussions (l'outil de recherche sur le forum n'aide pas trop, quand je recherche un ancien de mes messages, je le trouve pas. Mais sur Dvp, qui a un outil de recherche un peu mieux, les mêmes questions sont reposées 100 fois). Donc moi (ou un autre) expliqueront à nouveau au prochain qui demandera
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
Discord NaN. Mon site.
Discord NaN. Mon site.
Discord NaN. Mon site.
Discord NaN. Mon site.