|
Publicité ' | ||||||||||||||||||||||||
|
|
#21 |
|
Membre du Club
![]() Étudiant Inscription : janvier 2008 Messages : 234 ![]() |
Bonsoir,
Bon je tourne le problème dans tous les sens... il y a forcément une embuche quelque part. C'est épuisant ![]() Replaçons un peu les choses dans leur contexte. Vous êtes de formidables pédagogues, je ne pourrai cependant pas respecter à la lettre tout ce que vous indiquez. D'une part parce que j'en ai pas le temps, reprendre le métier dont je n'était censé produire qu'une adaptation n'est pas réaliste. D'autre part parce que je ne peux pas tout axer suivant ces principes. Amener un peu de pragmatisme dans tout ça ne serait vraiment pas de refus. Même si dans l'état actuel je semble être bien incapable de trouver une méthode systématique pour traduire mon PHP et mon Java en C++. Je pensais avoir atteint un certain stade dans le respect des principe objets avec Java mais il semble n'en être rien. De là à savoir où est la vérité, je suis bien incapable de le dire. Dans cet attente on va considérer les problèmes soulevés ici comme résolu, je devrais m'en sortir niveau pointeur/référence avec ce qui a été expliqué ici. Pour le reste, il faut que j'y réfléchisse encore. Merci et bonne continuation. [Edit] A noter que je suis déjà arrivé aux "certitudes" suivantes : 0. Principes généraux - Si type copiable = reference, si type non copiable = pointeur - Allocation dynamique obligatoire (sauf éventuellement dans le main)! I. Classes - Si type copiable, fournir obligatoirement le constructeur de copie - Attributs forcément privés (encapsulation), en référence/pointeur II. Getters / Setters (quand ils existent) - Les getters renvoient systématiquement des const references/pointeurs selon le type. - Les setters acceptent systématiquement des références/pointeurs (évite les copies). Pas de const sinon l'objet lui-même ne pourra pas disposer de son attribut théoriquement privé! III. Méthodes - Arguments systématiquement en référence/pointeurs selon le type IV. Collections - Membres obligatoirement pointeurs? -> Allocation dynamique des membres obligatoire Cela peut paraitre désuet... néanmoins ca va me permettre d'avancer un peu plus sereinement. [/edit] |
|
|
00
|
|
|
#22 | |||||||
![]() ![]() |
Citation:
![]() Citation:
Parce que c'est un retour de fonction et que la fonction crée l'objet polymorphe qu'elle renvoie parce que c'est un argument et que l'argument peut ne pas exister (ou qu'il n'existe aucun moyen de donner une valeur à cet argument qui représente la non existence) Citation:
constructeur par copie, opérateur d'affectation et destructeur obligatoires si l'implémentation fournie par défaut par le compilateur (comprends : celle que le compilateur fournira d'office si tu ne déclares pas ces fonctions) ne suffit pas. Si tu dois donner une implémentation particulière à l'une de ces fonctions, tu devras le faire pour les trois (règle de "trois grands") Les attributs sont bien privés, mais ce sont des valeurs, à moins que tu ne puisse pas faire autrement : Tu ne pourras pas faire autrement lorsque le membre en question fait référence à un objet polymorphe que tu ne connais que comme étant du type de base. A ce moment là, la décision d'utiliser un pointeur ou une référence dépendra de la possibilité que tu laisses à l'utilisateur de changer l'instance de l'objet en question: Si l'objet est défini une fois pour toute et qu'il ne faut plus mettre un autre objet à la place : autant autant (pour autant que faire se peut) utiliser une référence. Si tu peux décider d'assigner un autre objet en cours de route, ou si l'objet peut ne pas exister, ce sera un pointeur, parce que tu n'auras pas le choix. Citation:
Cela signifie que les mutateurs prennent... des références constantes, sauf si tu dois vraiment passer par un pointeur. En plus, un accesseur peut, éventuellement, se justifier si cela fait effectivement partie des services que tu es en droit d'attendre de la classe, mais un mutateur non : préfères fournir le nombre de fonctions qui vont bien et qui vont s'occuper elles-même de modifier ton membre de manière cohérente, en veillant à ce que leur nom indique clairement le genre de modification qui est faite. Citation:
Citation:
Le contenu des collections est, quant à lui, de préférence des valeurs, à moins que tu n'aies pas d'autre choix. Tu n'auras pas le choix si tu dois faire cohabiter des objets polymorphes en les faisant passer pour leur type de base. Dans ce cas, tu créeras des collections de pointeurs. Citation:
Cela apporte une fantastique liberté dans la manière d'envisager les choses, mais, le revers de cette liberté est que tu as aussi celle... de te tirer une balle dans le pied ![]() Ce qu'il faut bien comprendre, c'est qu'en java, tu crées d'office tes objets avec new de manière à permettre au garbage collector de les prendre en charge. En C++, tu ne dois recourir à l'allocation dynamique de la mémoire que si tu n'as pas d'autre choix. Et, encore une fois, les situations dans lesquelles tu n'as pas le choix sont toutes associées à l'héritage et au polymorphisme. Mais il faut être conscient du fait que le fait de passer par une allocation dynamique de la mémoire te rend responsable de la durée de vie de l'objet. C'est parfois ce que tu veux, pour pouvoir passer la barrière imposée par la portée automatique des variables (qui n'existent autrement qu'entre le moment de leur déclaration et le l'accolade fermante de la portée dans laquelle elles sont déclarée). Mais, quoi qu'il en soit, tu ne doit jamais écrire une étoile, prendre l'adresse d'une variable ou écrire un new à la légère : Si tu viens à prendre la décision de le faire, c'est parce que tu as déterminé que c'était définitivement la meilleure chose à faire
__________________
en bas de page
|
|||||||
|
|
00
|
|
|
#23 | |||||||
|
Membre du Club
![]() Étudiant Inscription : janvier 2008 Messages : 234 ![]() |
Citation:
Je ne vais pas déclarer des valeurs statiquement sur des attributs en sachant que je vais devoir traverser de multiples scopes alors que mes instances ne seront pas copiables/assignables. Devant ça je suis obligé d'utiliser l'allocation dynamique pour des types non copiables pour qu'ils survivent. Citation:
Une raison (bonne je ne sais pas) pourrait être de vouloir copier une instance d'entité pour justement ne pas la modifier. On la copie, on fait des modifications, on obtient un résultat et on répète ceci avec la même instance de base. Citation:
Donc mon attribut doit recevoir une référence vers cette instance... pour ne pas dire pointeur (en conséquence du recours à de l'allocation dynamique évoqué plus haut) Ceci vaut par extension à mes setters que je m’efforce d'éviter mais je ne vais pas non plus redéfinir des interfaces qui existent déjà (depuis quelques années pour certaines). Si l'assignalibilité des entités était admise, je pourrais déréférencer mon pointeur et l'assigner à l'attribut (quoi que c'est peut-être une copie). Citation:
Mes classes sont déjà écrites... les interfaces sont connues et elles comportent des setters. Dans le futur je pourrai corriger ça mais dans l'état je me cantonne à ce qui existe un maximum. Citation:
Il s'agit bien du contenu des collections qui est pointeur. Comme : Code :
std::map<std::string, const std::string *> Citation:
Le polymorphisme viendra surement plus tard, pour l'instant c'est uniquement les questions de scope. Citation:
Bonne fin d'après-midi. |
|||||||
|
|
00
|
|
|
#24 | |
|
Expert Confirmé Sénior
![]() ![]() Inscription : août 2004 Messages : 3 672 ![]() |
Bonjour,
je n'ai pas suivi toute la discussion, mais je ne peux pas laisser dire cela: Citation:
Le différence principale entre un pointeur et une référence est qu'un pointeur peut être null ou invalide; une référence pas. Une conséquence de cela est que, lorsque, dans une fonction, tu récupère un objet par référence, tu es sûr que ton objet est valide. Sur la copiabilité et l'assignabilité, il n'y a pas de différence "conceptuelle" entre les deux: cela ne dépend que de la classe qui est référencée/pointée. |
|
|
|
00
|
|
|
#25 | |
|
Membre Expert
![]() ![]() Inscription : novembre 2008 Messages : 973 ![]() |
Citation:
Si la durée de vie d’un objet n’est pas gérée par celui qui le crée, c’est louche (si c’est une factory, OK). Si tu ne sais pas définir la durée de vie de tes objets, c’est qu’il y a un problème de conception. Cela dit, dans une optique « pragmatique », comprendre « boucler tout ça dans les temps en faisant des concessions », c++ te fournit des outils qui peuvent t’aider : shared_ptr et unique_ptr. - unique_ptr te permet de transférer la responsabilité de la durée de vie d’un propriétaire à un autre. C’est pas une copie, c’est bien un transfert. C’est typiquement ce que peut renvoyer une factory : elle a créé un objet, et t’en donne la responsabilité. - shared_ptr te permet de partager la responsabilité de la durée de vie d’un objet entre plusieurs propriétaires : attention, s’il y a des cycles, il y aura des fuites mémoire. C’est par exemple ce que tu peux faire pour ta configuration partagée entre tes différents services. Mais attention : ces conseils sont là en « pis-aller », pour les bonnes pratiques, relis les messages de Koala jusqu’à ce que tu sois convaincu que c’est bien la bonne solution
__________________
HADOPI - Le Net en France : black-out |
|
|
|
00
|
|
|
#26 | ||
|
Membre du Club
![]() Étudiant Inscription : janvier 2008 Messages : 234 ![]() |
Citation:
Cependant tout semble être question de transfert de responsabilité et de durée de vie. Si je veux qu'un de mes objets survive à une à fermeture d'accolade ou un changement de scope je vais regarder ce que je peux en faire avant qu'il soit supprimé. Un objet copiable peut être retourné par valeur donc je peux l'initialiser statiquement. Un objet non copiable sera initialisé en allocation dynamique donc avec un pointeur. Tel était le sens de ma phrase. Citation:
Du reste je vais voir ce qu'il est possible de faire avec les pointeurs intelligents. Je viens de m’apercevoir que VC2012 ne gérait pas la délégation de constructeurs, gère-t-il ces pointeurs spécifiques de C++11? |
||
|
|
00
|
|
|
#27 | |
|
Expert Confirmé Sénior
![]() ![]() Inscription : août 2004 Messages : 3 672 ![]() |
Citation:
Un objet non copyable peut parfaitement être alloué sur la pile (en statique, comme tu dis). On peut même avoir plusieurs instances d'un tel objet, c'est juste que ces instances ne seront pas obtenues en faisant des copies, mais directement en appelant le constructeur. Il y a quelque chose qui cloche dans ta façon de voir ça, mais je ne parviens pas à trouver ce que c'est. Visual gère les unique_ptr depuis la version 2010 (donc 2012 également). |
|
|
|
00
|
|
|
#28 | ||
|
Membre du Club
![]() Étudiant Inscription : janvier 2008 Messages : 234 ![]() |
Citation:
Je parle de survivre à un changement de scope, pas d'y naitre et d'y mourrir. Forcément qu'un objet copiable ou non, si il ne doit exister que dans le scope d'une méthode sera initialisé sur la pile. Dans mon cas j'utilise des setters même si je suis un méchant hérétique. Des instances potentiellement non copiables vont être conservées dans les attributs d'autres instances. Si je créé ces instances sur la pile et que j'en donne la référence à mes setters, il va bien y avoir quelques problèmes. D'où l'utilité manifeste d'initialiser mes objets de type non copiable par allocation dynamique. Est-ce plus clair? Citation:
|
||
|
|
00
|
|
|
#29 |
![]() ![]() Cyrille Network programmer Inscription : juin 2010 Messages : 1 551 ![]() |
J'ai l'impression que ça tourne en rond et au dialogue de sourd cette histoire...
Et que ce que tu décris ce n'est rien de plus qu'une factory, qui délègue la responsabilité de l'objet créé à son appelant qui le récupère. Et dans ce cas oui, l'allocation dynamique est obligatoire. Mais ce n'est pas la seule option. Une factory peut très bien conserver la responsabilité de l'objet, l'appelant n'ayant alors pas à se soucier de sa durée de vie. Retourner un shared_ptr par exemple, ou une référence/pointeur d'un objet qu'il alloue lui-même (sous forme de static le plus souvent), mise en place d'un compteur de référence et objet qui se suicide, ... Mais je n'ai encore jamais vu de factory qui retourne un objet par copie... ça existe et a peut-être une raison d'être, mais je n'ai jamais rencontré ce cas. Techniquement en tous cas rien ne l'empêche, mais conceptuellement j'en vois pas l'intérêt. |
|
|
00
|
|
|
#30 | |||
|
Membre du Club
![]() Étudiant Inscription : janvier 2008 Messages : 234 ![]() |
Citation:
Code :
J'ai mis l'attribut _tc de TypeQuelconque en valeur comme le préconise koala. J'ai cependant pas d'autre solution que de définir en pointeur _tnc pour éviter une copie fatale dans le setter. Ici le setter n'est pas une utilisation recommandée et je le sait. C'est valable pour tout autre méthode qui accepterait en paramètre un objet de type TypeNonCopiable et qui stockerait le tout dans un attribut privé de la classe. C'est ce qui va se passer chez moi. Au moins je me passe de copier des objets entité. A voir ensuite comment je peux améliorer tout ça avec des pointeurs intelligents. Cela parait-il enfin plus clair? |
|||
|
|
00
|
|
|
#31 | |||||||
![]() ![]() |
Citation:
Les seules différence qu'il y ait entre une référence et un pointeur (hormis la syntaxe pour les utiliser) tient dans le fait qu'un pointeur peut etre nul (il peut ne pas y avoir d'objet "effectif"), alors qu'une référence est d'office l'alias d'un objet existant Citation:
Cela ne t'empêche absolument pas d'avoir plusieurs objets de type "personne", le premier représentant fanfouer, le second bousk, le troisième koala et le dernier tarantpion, mais tu ne vas jamais copier ni bousk, ni fanfouer, ni tartanpion (et encore moins koala : je revendique mon unicité Tout ce qui ne devra pas gérer la durée de vie de ces personnes vas les utiliser de préférence sous la forme de références (constante si ca ne doit pas les modifier) ou, s'il faut exprimer le fait que la personne peut ne pas exister, sous la forme de pointeur. Citation:
Citation:
Citation:
Il est beaucoup plus facile de reprendre une conception lorsqu'elle est encore au stade de phrases / petits dessins, et que rien n'est encore utilisé dans le code que de reprendre la conception (car il s'agit de cela, nous sommes bien d'accord Citation:
Il n'y a que pour les type ayant sémantique d'entité pour lesquels tu n'auras pas le choix et que tu devras utiliser les pointeurs (std::map<identifiant, personne*> si tu veux pouvoir placer des personnes, des clients et des fournisseurs en les considérant simplement comme des personnes) Citation:
Tu n'auras un problème de scope qu'à partir du moment où tu ne peux pas copier ton objet,or, si un objet est non copiable, c'est parce qu'il a sémantique d'entité. Et, si un type a sémantique d'entité, il apparait être un candidat idéal à intervenir dans une hiérarchie de classes. Ce n'est peut etre pas le cas présentement, mais cela peut le devenir au fur et à mesure de l'évolution des besoins Alors, tu peux te baser sur le fait que ta classe n'intervient (pour l'instant) pas dans une hiérarchie de classe, mais est non copiable, pour utiliser ce type comme membre par valeur dans une classe particulière (qui sera elle-même non copiable ![]() Et à ce moment là, tu seras de toutes manières obligé de "tout casser" pour arriver à prendre en compte le polymorphisme. Ma conclusion personnelle est que, si tu te trouves face à une classe ayant clairement sémantique d'entité, il est préférable de partir du principe que cette classe interviendra tot ou tard dans une hiérarchie de classe, et d'agir comme si c'était déjà le cas, pour faciliter les évolutions futures C'est pour cela que je dis que le polymorphisme (en fait, c'est même carrément tout le concept de substituabilité qui est en cause
__________________
en bas de page
|
|||||||
|
|
00
|
|
|
#32 | |||
|
Membre Expert
![]() ![]() Inscription : novembre 2008 Messages : 973 ![]() |
Citation:
Mais c’est bien le fondement de ton problème : tes setteurs, quelle est leur sémantique ? - prendre la responsabilité de l’objet ? - prendre une référence vers un objet dont ils n’ont pas la responsabilité ? - partager la responsabilité de l’objet ? Si tu sais répondre à cette question, tu sais : - comment faire pour que ton programme soit correct, càd, respecter toujours la sémantique définie. Si cette sémantique n’est pas définie, tu peux être sûr qu’elle est à géométrie variable en fonction des endroits, et là c’est le drame. - quels smart pointer utiliser afin de garantir le respect de cette sémantique. Et tu as réglé 95% de tes problèmes. Par rapport à tes interrogations sur les pointeurs/références, en ce qui concerne les données membres, le seul cas où tu peux mettre une référence, c’est : - mon objet ne gère pas la responsabilité de son membre référencé - le membre existe avant l’instantiation de l’objet, et je sais garantir qu’il existera tout au long de cette vie. Ce n’est finalement pas si courant que ça. Pour les autres cas, tu n’as pas le choix : c’est un des pointeurs intelligents que tu devras utiliser, en fonction de la sémantique associée (comprendre : qui gère la durée de vie). Citation:
__________________
HADOPI - Le Net en France : black-out |
|||
|
|
00
|
|
|
#33 | ||
|
Membre du Club
![]() Étudiant Inscription : janvier 2008 Messages : 234 ![]() |
Bonjour à tous.
J'ai été absent tout ce week end, sans pouvoir poursuivre cet échange. Citation:
Prenons ton exemple sur les personnes. Je créé un objet Personne vide, je lui attribue quelques propriétés standard puis je copie cette instance tour à tour pour créer les personnes fanfouer, bousk et koala qui auront tous les traits que j'ai défini précédemment. Par exemple pour batcher des créations de profil dans des gros annuaires. A tout instant, les instances sont uniques puisque qu'elles soient vides ou aient un nom, ce ne sont pas les mêmes personne ni les mêmes adresses en mémoire. Après tout, on a interdit la copie d'instance ayant sémantique d'entité... pas de tout définir comme ayant sémantique de valeur Citation:
Dans l'état actuel je n'en ai pas le temps. Pourquoi ne pas directement utiliser un HipHop à la Facebook dans ce cas? Ok pour les question de polymorphisme. Concernant le post de white_tentacle, j'avoue que je n'ai jamais pensé en terme de responsabilité... ni de destructeur à vrai dire. Cette approche n'a jamais été abordé dans le peu de cours que j'ai eu en objet et le reste correspond à de l’autodidaxie. Il faut donc que je me pose ces questions maintenant, je vois à peu près l'année 2040 se profiler pour la fin du projet. Bonne après midi. |
||
|
|
00
|
|
|
#34 |
![]() ![]() |
Dis toi qu'il est possible de faire en C++ et dans un même projet des choses de très haut niveau et des choses de très bas niveau.
La grosse différence avec java ou C#, c'est que dés que tu écris new quelque part, tu deviens responsable de la durée de vie de la variable qui y est associée. Cette variable, elle utilise des ressources, mais ce n'est pas la seule. Même si les ordinateurs ont de plus en plus de ressources et même si la mémoire n'est jamais qu'une sorte de ressource particulière, tes ressources restent malgré tout limitées, et tu n'as pas un ramasse miette qui vient faire le ménage à ta place. Si tu maintiens en mémoire quelque chose qui est devenu inutile, il n'y a rien à faire, ce sont des ressources dont tu ne disposes plus pour la suite. Pour pourvoir travailler, on a tenté de t'expliquer qu'il faut que l'objet existe par ailleurs et que tu es donc soumis à certains impératifs vu que, a priori, un objet créé sans avoir recours à l'allocation dynamique n'existe que jusqu'à ce que l'on atteigne l'accolade fermante dans laquelle il a été déclaré (ou, si c'est une variable membre d'un autre objet, jusqu'à ce que l'objet qui le contient soit lui-même détruit selon le même principe). Dés lors, tu devras effectivement parfois passer par l'allocation dynamique pour t'assurer qu'un objet continue à exister au delà du bloc d'accolade dans lequel il est créé, mais cela pose un autre problème : qui va s'occuper de le détruire, quand et à quelle condition Les problèmes références VS pointeurs sont, j'ai presque envie de dire, purement facultatifs tant que tu n'as pas compris ces quelques lignes
__________________
en bas de page
|
|
|
00
|
|
|
#35 | |
|
Expert Confirmé
![]() Pierre Ingénieur développement logiciels Inscription : juin 2007 Messages : 1 211 ![]() |
Citation:
La classe Personne a une sémantique d'entité. fanfouer est fanfouer, quelle que soit la manière d'obtenir une variable le nommant. Tu crées trois personnes convenablement constituées, pas trois tas de chair informes que tu sculpteras à la volée. C'est le principe de base d'un constructeur correct: un objet est construit dans un état valide utilisable ou n'est pas construit du tout (cf RAII).
__________________
Mes principes de bases du codeur qui veut pouvoir dormir:
|
|
|
00
|
|
|
#36 |
|
Membre du Club
![]() Étudiant Inscription : janvier 2008 Messages : 234 ![]() |
Bonsoir,
Bon je pense donner le mot final ce soir. J'ai tenu compte de vos conseils et identifié les classes qui avaient sémantiques d'entité. Elles ne sont donc pas copiables comme expliqué. Pour le reste j'ai du conserver mes interfaces, avec les couples getter/setter qui vont avec. On verra pour les factories lors de prochaines versions (ou quand les bugs apparaitront c'est selon Mes arguments sont tous en référence ou en pointeurs, les attributs en valeur ou en pointeur selon la sémantique. Je regarde toujours pour les pointeurs intelligents mais c'est vraiment intéressant. Donc merci, mon problème semble résolu, je devrais faire un petit bout de chemin avec c++ prochainement A bientôt |
|
|
00
|
Copyright © 2000-2013 - www.developpez.com