Je débute avec la programmation POO en C++, on me demande d'implémenter une classe*qui ne pourrait pas être instanciée sans un nom de type std::string.
J'avoue que là, je suis un peu perdu sur comment je dois le faire.
Merci d'avance.
Je débute avec la programmation POO en C++, on me demande d'implémenter une classe*qui ne pourrait pas être instanciée sans un nom de type std::string.
J'avoue que là, je suis un peu perdu sur comment je dois le faire.
Merci d'avance.
Bonjour,
Ce n'est pas très clair. S'agit-il de déclarer et dériver une classe abstraite ? Peut-on avoir l'énoncé exact (et original) de ton problème ?
Voici l'énoncé exacte sur l'image ci-dessous.
D'accord, il s'agit d'une classe « Élève » et comme l'instancier revient à représenter un élève donné en mémoire, on attend d'elle qu'elle contienne le minimum d'information nécessaires à identifier un individu, donc en l'occurrence le nom de l'élève.
Hors contexte, on avait du mal à savoir où ils voulaient en venir, surtout si c'est à l'intention des débutants. S'ils comptaient dès le départ te faire écrire une classe template, ça aurait déjà été velu, s'ils avaient trouvé un moyen efficace de faire de l'introspection, c'est moi que ça aurait intéressé.
Je pense que ta classe doit tout simplement contenir un membre « name » de type std::string, donc ici :
Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 class Student { public: std::string name; };
… et pour qu'il soit nécessaire de le renseigner, il te faut un constructeur qui accepte une chaîne de type std::string en argument et qui s'en servent pour affecter ce nom au champ name. Tu écris alors ce constructeur et aucun autre pour le moment (en particulier, pas de constructeur par défaut), si bien qu'il sera effectivement nécessaire de passer ce nom à l'instanciation en l'absence de toute autre méthode de construction.
On reste volontairement flou ici s'il s'agit d'un exercice car tu dois avoir les billes pour le faire. Dans le cas contraire, on développera un peu plus.
Bon courage.
Je comprends que la classe ne doit pas avoir de constructeur par défaut.
Je ne suis pas habitué à ce qu'on mette en avant cette contrainte dans des exos d'initiation. C'est plus souvent une conséquence due à un autre choix. Même s'il est vrai que c'est un choix de conception pertinent.
Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...
Merci beaucoup pour votre aide, je vais suivre cette piste puis je verrai les retours de la moulinette.
Encore une fois merci.
Salut,
Arf, Obsidian est parti trop vite, il a laissé ses meilleurs joueurs sur le banc de touche: private et const.
On comprend bien la notion de snippet, mais quelqu'un qui débute peut y voir une mesure incitative, du coup, on les fait participer.
Et si ça a été enseigné, il conviendra aussi de s'interroger si le constructeur doit être spécifié explicit et s'il doit être défini en dehors de sa classe.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 class Student { public: //... private: const std::string name; }
C'est mieux, quitte à relaxer plus tard et comprendre pourquoi, que l'inverse.
Effectivement, le private manque.
En revanche, le const sur les attributs est pour moi une pratique exogène, typique du Java, mais vraiment impropre en C++. Les const on va plutôt les mettre à l'extérieur: sur l'instance de la classe. C'est vraiment trop restrictif en général.
> > Je ne suis pas habitué à ce qu'on mette en avant cette contrainte dans des exos d'initiation.
> C'est mieux, quitte à relaxer plus tard et comprendre pourquoi, que l'inverse.
Tout à fait. Et c'est quelque chose que je fais systématiquement. C'est juste que je n'ai jamais envisagé de l'intégrer explicitement comme une contrainte d'exo. En général, je l'enseigne indirectement: d'abord en faisant constater que la présence de n'importe quel constructeur retire celui par défaut. Ensuite que la bonne pratique est que tout objet doit être immédiatement utilisable une fois construit, et qu'il ne faut jamais qu'on ait besoin de se poser la question de savoir si l'objet est entièrement initialisé. Et en conséquence: le constructeur par défaut sera malvenu bien souvent -- surtout sur les classes qui ne sont pas des classes valeurs.
Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...
Bonjour à tous,
Je ne l'ai pas oublié, je l'ai fait exprès car les membres d'une classe sont private par défaut (contrairement à ceux d'une struct). L'idée ici était d'avoir un membre qu'il puisse lire en retour, même de l'extérieur, pour éviter d'ajouter de la complexité à la complexité.
Sinon, si on souhaite être exhaustif dès le départ, on ajoute « protected » à la liste et on les voit tous en une fois, mais ce n'est pas l'objet de l'exercice ici. Distinguer les différents types de constructeurs en C++ n'est pas une chose triviale quand on débute totalement en programmation orientée objet…
Et si on veut vraiment ergoter : « quel est l'intérêt fondamental d'avoir des membres private à la base ? ». La réponse existe également, mais elle n'est pas du tout évidente non plus, surtout compte tenu des technologies vues au préalable et depuis lesquelles on en vient à notre problème.
En faisant cela on perd l'affectation, et donc la possibilité de stocker le type dans des vecteurs via push_back.
C'est pour cela qu'il est plus classique de mettre les const à l'extérieur en C++ et pas à l'intérieur.
Bon cet argumentaire, c'est pour les valeurs, et aussi les pures capsules RAII (unique pointer, lock...). Pour les entités c'est sûr que c'est discutable vu que l'on va quasi systématiquement les manipuler via des indirections. Mais... on ne peut plus les mettre dans des variants que l'on voudrait affecter non plus.
Bref, mettre le const sur les variables membres, ce n'est pas si anodin.
Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...
Salut,
Je n'ai pas eu l'occasion de revenir sur cette discussion, et je ne m'attendais pas à lire ce message.
Je reprécise, au cas où, que le "const" que j'avais ajouté était là seulement pour mettre le compilateur en alerte, ne laissant au posteur original, qui est très loin de toutes ces considérations, aucun autre choix que celui de réussir son exercice.
Après, que soit évoqué un souci d'affectation (bien que c'est un faux problème), pourquoi pas. Mais pour ce qui est d'un lien direct avec push_back(), ça, ça me laisse perplexe.
En fait, @Luc, tu abordes là un problème de copie qui ne me semble pas très logique. En effet, pour qu'il y ait affectation, il faut que l'objet existe. Or, quand tu fais ton push_back(), ce n'est pas encore le cas.
Ou alors, c'est un problème qui est propre à un style de programmation classique, donc, avant C++98, ou dans les C++98/03. Au-delà, c'est juste impossible.
Je rappelle qu'en termes de syntaxe, il y a 2/3 types d'affections, X x= X(...), X y= x et x= ..., et seule la dernière appelle l'opérateur d'affectation de copie. C'est cette forme qui n'est pas générée par le compilateur, ici, à cause de cette donnée membre "const".
Bon, juste avant d'envoyer ce message, j'ai fait un test rapide avec gcc15/c++98/(rvo et copy-elision désactivées). Résultat, push_back() n'a pas posé de problème particulier. C'est passé par le constructeur de copie le plus normalement possible.
Mais j'ai quand même voulu essayer avec un compilateur désuet. Alors j'ai téléchargé borland bcc55 (2000).
Là, effectivement, la compilation a été bloquée. Cependant, en lui fournissant le constructeur de copie, tout est redevenu normal. Sauf que ce comportement n'est pas conforme au standard. C'est-à-dire que le compilateur aurait dû générer ce constructeur de copie lui-même.
Qu'importe, j'ai continuait en ligne avec GCC4.7.1 (2012). Lui génère bien le constructeur de copie, mais réclame quand même un opérateur d'affectation de copie qu'il n'utilise même pas! Quant à GCC 7.1 (2017), tout est OK.
En conclusion, ton problème était lié à une aberration dans C++98, et uniquement C++98, et elle a été corrigé: https://cplusplus.github.io/LWG/issue276
Salut,
En effet. Au temps pour moi. L'erreur sur push_back est corrigée à partir de gcc 7. Et j'en était resté là. Effectivement, tu as raison, le changement de capacité provoque des créations et non des affectations, et donc refuser l'insertion à la fin était un non sens.
Après, le problème reste sur insert() (EDIT: et erase, je soupçonne -- pas vérifié) (ouf?) https://godbolt.org/z/5EK5vdr9b
Mais ça m'apprendra à me poser pour réfléchir la prochaine fois.![]()
Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...
Partager