IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C++ Discussion :

Conception de classes et pointeurs


Sujet :

C++

  1. #21
    Membre régulier
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Points : 84
    Points
    84
    Par défaut
    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]

  2. #22
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par fanfouer Voir le message
    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 :
    Sauf que... très peu de ces certitudes sont justes, ni même à peine fondées
    0. Principes généraux
    - Si type copiable = reference, si type non copiable = pointeur
    - Allocation dynamique obligatoire (sauf éventuellement dans le main)!
    Non: toujours par référence, sauf si tu as une bonne raison de faire autrement :
    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)
    I. Classes
    - Si type copiable, fournir obligatoirement le constructeur de copie
    - Attributs forcément privés (encapsulation), en référence/pointeur
    Encore faux :
    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.

    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é!
    j'ai dit : tout le temps par référence sauf si tu as une raison de faire autrement!!!

    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.
    III. Méthodes
    - Arguments systématiquement en référence/pointeurs selon le type
    Je me répètes encore : toujours par référence, à moins que tu n'aies pas le choix, par exemple, parce que tu dois indiquer la possibilité de la non existence de l'objet passé en paramètre

    IV. Collections
    - Membres obligatoirement pointeurs?
    -> Allocation dynamique des membres obligatoire
    non: les collections membres sont toujours des valeurs

    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.
    Cela peut paraitre désuet... néanmoins ca va me permettre d'avancer un peu plus sereinement.
    [/edit]
    Surtout que Java et PHP sont testés idiot proof, alors que C++ part du principe que le développeur sait ce qu'il fait.

    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
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  3. #23
    Membre régulier
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Points : 84
    Points
    84
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Non: toujours par référence, sauf si tu as une bonne raison de faire autrement :
    Toute la distinction référence/pointeur se fait sur la copiabilité/assignabilité du type.
    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.

    Encore faux :
    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")
    On est d'accord. Si je précisais de fournir ces constructeurs, c'est uniquement parce que C++ ne connait que la shallow-copy. Si je veux faire des deep-copies, je vais être oblig" de l'implémenter.

    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.

    Les attributs sont bien privés, mais ce sont des valeurs, à moins que tu ne puisse pas faire autrement :
    Si un de mes attributs est de type entité et qu'une instance d'entité est fournie au constructeur pour servir d'initialisation, je ne pourrai pas la copier.
    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).

    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.
    Nous sommes d'accord.
    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.

    non: les collections membres sont toujours des valeurs

    Le contenu des collections est, quant à lui, de préférence des valeurs, à moins que tu n'aies pas d'autre choix.
    On parlait bien de la même chose, je me suis mal exprimé.
    Il s'agit bien du contenu des collections qui est pointeur. Comme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::map<std::string, const std::string *>
    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).
    C'est précisément pour ça que je parle de pointeur dans tout ce qui précède.
    Le polymorphisme viendra surement plus tard, pour l'instant c'est uniquement les questions de scope.

    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
    Ce sera surement la meilleure chose à faire parce que je serai bloqué par la rigidité des design pattern exposés.

    Bonne fin d'après-midi.

  4. #24
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Bonjour,

    je n'ai pas suivi toute la discussion, mais je ne peux pas laisser dire cela:
    Citation Envoyé par fanfouer Voir le message
    Toute la distinction référence/pointeur se fait sur la copiabilité/assignabilité du type.
    car cela me parait être une erreur de compréhension très lourde.

    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.
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  5. #25
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Citation Envoyé par fanfouer Voir le message
    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.
    Ton problème, c’est que tu reprends du code java/php, qui, comme la plupart du code java/php, se contrefout de quelque chose de pourtant fondamental : la durée de vie des objets.

    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 (ce qui va imposer de remettre en question beaucoup de choses par rapport à tes habitudes de java/php)..

  6. #26
    Membre régulier
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Points : 84
    Points
    84
    Par défaut
    Citation Envoyé par r0d Voir le message
    Bonjour,

    je n'ai pas suivi toute la discussion, mais je ne peux pas laisser dire cela:car cela me parait être une erreur de compréhension très lourde.
    Ce n'est pas un critère de choix direct non on est d'accord.

    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 Envoyé par white_tentacle
    Ton problème, c’est que tu reprends du code java/php, qui, comme la plupart du code java/php, se contrefout de quelque chose de pourtant fondamental : la durée de vie des objets.
    Ils gèrent ça différemment. Le garbage collector permet de s'affranchir de certaines problématiques, je ne dirais pas que le langage entier s'en contrefou.

    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?

  7. #27
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par fanfouer Voir le message
    Un objet non copiable sera initialisé en allocation dynamique donc avec un pointeur. Tel était le sens de ma phrase.
    ouille... je t'en prie, ne le prend pas mal mais là tu confond tout
    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.


    Citation Envoyé par fanfouer Voir le message
    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?
    Visual gère les unique_ptr depuis la version 2010 (donc 2012 également).
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  8. #28
    Membre régulier
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Points : 84
    Points
    84
    Par défaut
    Citation Envoyé par r0d Voir le message
    Il y a quelque chose qui cloche dans ta façon de voir ça, mais je ne parviens pas à trouver ce que c'est.
    C'est juste qu'on ne se comprend pas.

    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?

    Visual gère les unique_ptr depuis la version 2010 (donc 2012 également).
    Bien, donc il y a quelque chose à faire de ce côté.

  9. #29
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 965
    Points
    32 965
    Billets dans le blog
    4
    Par défaut
    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.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  10. #30
    Membre régulier
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Points : 84
    Points
    84
    Par défaut
    Citation Envoyé par Bousk Voir le message
    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.
    Non pas encore. Voici un peu de code, on se comprendra peut-être mieux :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
     
    // Les headers d'une classe TypeQuelconque
    class TypeQuelconque {
       private: TypeNonCopiable *_tnc; // Déclaré en pointeur pour éviter une copie dans le setter ci-après
       private: TypeCopiable _tc;
     
       public: const TypeNonCopiable &getTnc (void) const;
       public: void setTnc (TypeNonCopiable *tnc);
     
       public: const TypeCopiable &getTc (void) const;
       public: void setTc (TypeCopiable &tc);
    }
     
    // Cpp de TypeQuelconque
    const TypeNonCopiable &TypeQuelconque::getTnc(void) const {
       return *this->_tnc; // Ici le déréférencement n'est peut-être pas la meilleure des choses à faire, mais je me sépare de mon pointeur dès que possible.
    }
    void TypeQuelconque::setTnc (TypeNonCopiable *tnc){
       this->_tnc = tnc; // Pas de copie grâce aux pointeurs
    }
     
    const TypeCopiable &TypeQuelconque::getTc(void) const {
       return this->_tc;
    }
    void TypeQuelconque::setTnc (TypeCopiable &tnc){
       this->_tc = tc; // Copie
    }
     
     
    // Cpp d'une classe de test (ouai comme en Java, mais si je le fait dans le main() mon exemple n'a plus de sens).
    void A::methode (TypeQuelconque &obj){
       TypeNonCopiable test_nonCopiable;
       TypeCopiable test_copiable;
     
       obj.setTnc(test_nonCopiable); // Aïe on envoie la référence d'un truc qui va expirer juste après.
       obj.setTc (test_copiable); // Idem!
    }
     
    // Du coup avec l'allocation dynamique on lève le problème de scope déjà :
    void A::methode (TypeQuelconque &obj){
       TypeNonCopiable *test_nonCopiable = new TypeNonCopiable();
       TypeCopiable *test_copiable = new TypeCopiable();
     
       obj.setTnc(test_nonCopiable); // Là notre objet va survivre
       obj.setTc(*test_copiable);
    }
    A::methode est peut-être une factory, mais elle ne délègue pas la responsabilité des deux objets test_copiable et test_nonCopiable à son appelant. Enfin j'ai pas l'impression... elle renvoie void.

    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?

  11. #31
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par fanfouer Voir le message
    Toute la distinction référence/pointeur se fait sur la copiabilité/assignabilité du type.
    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.
    Non, une référence et un pointeur font, en gros, la même chose : cela évite la copie d'une variable en fournissant, pour la référence un alias de l'objet et pour le pointeur l'adresse mémoire à laquelle l'objet se trouve.

    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
    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.
    Tu n'as pas à copier un instance d'objet ayant sémantique d'entité.

    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.

    Si un de mes attributs est de type entité et qu'une instance d'entité est fournie au constructeur pour servir d'initialisation, je ne pourrai pas la copier.
    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)
    Effectivement, tu passes là dans le cas "si tu n'as pas d'autre choix" .
    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).
    Pourquoi pas, si tu es dans une phase de passage à java ==> C++, c'est peut etre l'instant rêvé de faire les choses correctement

    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.
    J'aurais tendance à faire l'inverse : revoir la conception pour corriger les problème et coder cette conception "corrigée".

    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 ) alors que tu as déjà toute une base de code existante et dans laquelle le moindre changement peut se répercuter sur des dizaines de fonctions
    On parlait bien de la même chose, je me suis mal exprimé.
    Il s'agit bien du contenu des collections qui est pointeur. Comme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::map<std::string, const std::string *>
    std :: string a sémantique de valeur, donc, ce sera une map de valeur (
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::map <std::string, std::string>
    )

    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)

    C'est précisément pour ça que je parle de pointeur dans tout ce qui précède.
    Le polymorphisme viendra surement plus tard, pour l'instant c'est uniquement les questions de scope.
    Ce que tu n'as pas l'air de comprendre, c'est que le polymorphisme et les problèmes de scope sont intimement liés...

    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 cela va fonctionner ... jusqu'au moment où tes besoins ayant évolué, tu auras besoin de dériver cette classe

    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 ) est intimement lié au problème de portée
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  12. #32
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Citation Envoyé par fanfouer Voir le message
    Citation Envoyé par white_tentacle
    Ton problème, c’est que tu reprends du code java/php, qui, comme la plupart du code java/php, se contrefout de quelque chose de pourtant fondamental : la durée de vie des objets.
    Ils gèrent ça différemment. Le garbage collector permet de s'affranchir de certaines problématiques, je ne dirais pas que le langage entier s'en contrefou.
    Le garbage collector est un truc global qui gère lui-même la durée de vie des objets : ça marche à peu près pour la RAM, pour tout le reste ça mène à des catastrophes.

    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).


    A::methode est peut-être une factory, mais elle ne délègue pas la responsabilité des deux objets test_copiable et test_nonCopiable à son appelant. Enfin j'ai pas l'impression... elle renvoie void.
    Que fait le destructeur de obj ? Est-ce qu’il libère typeNonCopiable ? Si c’est le cas, alors il y a bien transfert de responsabilité. Si ce n’est pas le cas, alors, qui est en charge de la durée de vie de typeNonCopiable ? Personne ? Ça c’est un gros problème ! (même en java : suppose que typeNonCopiable tienne un handle de fichier, il sera libéré au petit bonheur la chance, peut-être dans 3 jours ou 3 mois si le programme est un service).

  13. #33
    Membre régulier
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Points : 84
    Points
    84
    Par défaut
    Bonjour à tous.

    J'ai été absent tout ce week end, sans pouvoir poursuivre cet échange.

    Citation Envoyé par koala01 Voir le message
    Tu n'as pas à copier un instance d'objet ayant sémantique d'entité.

    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é ).
    Donc visiblement, le fait d'avoir des objets "motifs", ne doit pas être bien vu.

    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

    Pourquoi pas, si tu es dans une phase de passage à java ==> C++, c'est peut etre l'instant rêvé de faire les choses correctement

    J'aurais tendance à faire l'inverse : revoir la conception pour corriger les problème et coder cette conception "corrigée".
    ... Pour devoir propager ces approches ensuite dans les autres implémentations de mes librairies.
    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.

  14. #34
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    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
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  15. #35
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Citation Envoyé par fanfouer Voir le message
    Bonjour à tous.
    Je créé un objet Personne vide, je lui attribue quelques propriétés standard puis je copie cette instance tour à tour pour créer [des] personnes[...]
    Pas du tout.
    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:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  16. #36
    Membre régulier
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Points : 84
    Points
    84
    Par défaut
    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

+ Répondre à la discussion
Cette discussion est résolue.
Page 2 sur 2 PremièrePremière 12

Discussions similaires

  1. [POO] conception des classes
    Par poukill dans le forum C++
    Réponses: 229
    Dernier message: 19/07/2006, 08h28
  2. [conception] reprise classes mal pensées
    Par in dans le forum Général Java
    Réponses: 8
    Dernier message: 05/06/2006, 13h45
  3. Conception de classe
    Par Azharis dans le forum C++
    Réponses: 9
    Dernier message: 17/12/2005, 10h15
  4. Classe, pile, pointeurs et casse-tête!
    Par zazaraignée dans le forum Langage
    Réponses: 6
    Dernier message: 26/09/2005, 16h57
  5. probleme de conception de classe
    Par NhyMbuS dans le forum C++
    Réponses: 2
    Dernier message: 08/05/2005, 17h10

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo