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 :

Comment copier correctement un attribut pointeur qui a des classes filles


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Janvier 2003
    Messages
    229
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2003
    Messages : 229
    Par défaut Comment copier correctement un attribut pointeur qui a des classes filles
    Bonjour,

    je suis face à un problème objet et je n'arrive pas à trouver la solution, j'ai du trop faire de java et perdre quelques notions de polymorphisme
    En tout cas je "bloque" !!

    J'ai les classes suivantes :

    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
     
    class TType {
    public:
    	int entier;
    };
     
    class TTypeA : public TType {
    public:
    	int entierA;
    	char * chaine;
     
    	TTypeA() {
    		entierA = 8;
    		chaine = "toto";
    	};
    };
     
    class TTypeB : public TType {
    public:
    	int entierB;
    };
     
    class TData {
    public:
    	TData(){};
     
    	int entierData;
    	TType * type;
    };
     
    class TDataFille : public TData {
    public:
    	int entierFille;
     
    	TDataFille(TData * data) {
    		this->entierData = data->entierData; // ok
    		this->type = new TType(); // comment dupliquer proprement, c'est à dire que la copie se fasse aussi au niveau de la classe fille instanciée???
    	}
    };
    et le code suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    	TData * data = new TData();
    	TTypeA *typeA = new TTypeA();
    	data->type = typeA;
    	TDataFille *fille = new TDataFille(data);
     
    et ailleurs :
    	TTypeA * a = (TTypeA*)fille->type;
    Je voudrais que a soit une parfaite copie de typeA, c'est à dire que l'attribut entierA vale 8 et que chaine vale "toto" (ce qui n'est pas le cas bien sûr avec le code ci dessus !)

    merci beaucoup pour votre aide, j'ai hâte de voir où je bloque!!
    Pascale38

  2. #2
    Membre habitué
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2011
    Messages
    13
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2011
    Messages : 13
    Par défaut
    Il te faut une fonction virtuelle clone() dans ta classe de base et ses classes dérivées.


    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
    class TType {
    public:
    	int entier;
    	virtual TType* clone() const { return new TType(*this); }
    };
     
    class TTypeA : public TType {
    public:
    	int entierA;
    	char * chaine;
     
    	TTypeA() {
    		entierA = 8;
    		chaine = "toto";
    	};
    
    	virtual TType* clone() const { return new TTypeA(*this); }
    };
     
    class TTypeB : public TType {
    public:
    	int entierB;
    	virtual TType* clone() const { return new TTypeB(*this); }
    };

  3. #3
    Membre confirmé
    Profil pro
    Inscrit en
    Janvier 2003
    Messages
    229
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2003
    Messages : 229
    Par défaut
    et ensuite dans mon constructeur copie :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    
    TDataFille(TData * data) {
    	this->entierData = data->entierData;
    	this->type = data->type->clone();
    }
    trop trop fort, c'est beau !!
    Ca marche nickel !
    je n'avais jamais eu à utiliser clone() en fait jusqu'à présent, je n'aurai donc jamais trouvé toute seule ! Comme quoi on en apprend toujours...
    merci beaucoup pour cette belle et super rapide réponse !!!
    Hop résolu

    Pascale38

  4. #4
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    Attention toutefois :


    A priori, une classe dont un membre serait un pointeur vers un objet polymorphe risque fort d'avoir une sémantique d'entité, et donc, de ne pas être plus copiable que le membre en question

    En effet, les objets polymorphes sont des types qui ont, systématiquement, sémantique d'entité et qui sont donc incompatibles avec la notion de copie (même s'il peuvent être clônables )

    Or, tu ne peux décemment envisager de copier un objet que si tu es en mesure... de copier correctement l'ensemble de ses membres :

    S'il y a un seul de ses membres que tu ne peux pas copier, tu ne pourras pas copier correctement ton objet

    Si, de plus, l'objet en question est d'un type polymorphe, tu as d'autant plus de raisons de le rendre non copiable!!!

    En outre, quand on parle de pointeur, on parle, de manière quasi systématique, de gestion dynamique de la mémoire "quelque part" autour du pointeur.

    si tu donne la responsabilité de cette gestion dynamique de la mémoire à ton objet, tu prends une voie royale pour en arriver à ne pas respecter l'un des pilliers de la programmation qui est... la responsabilité unique.

    En résumé :
    1. Commence déjà par t'assurer qu'il est bel et bien cohérent de rendre le membre clônable (ce qui peut etre rendu nécessaire, par exemple, dans l'optique de la mise au point d'un mécanisme de undo / redo )
    2. S'il est cohérent de rendre le membre clonable, inquiète toi de savoir s'il est effectivement cohérent de faire en sorte que ce soit ton objet qui ait la responsabilité de ce clonage (l'idéal restant quand meme de veiller à ce que les objets ne soient pas clonés "n'importe où" )
    3. Enfin, si le membre en question est effectivement clonable et qu'il est effectivement cohérent de donner la responsabilité du clonage à ton objet (en sachant que, du coup, tu ne devrais plus lui donner d'autre responsabilité ), alors, tu peux envisager de rendre ton objet également clonable (mais non copiable), pour autant que cela ait un sens de le 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

  5. #5
    Membre confirmé
    Profil pro
    Inscrit en
    Janvier 2003
    Messages
    229
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2003
    Messages : 229
    Par défaut
    Salut Koala01,
    hum là j'avoue que tu dépasses et de loin mes connaissances en la matière, notamment lorsque tu parles d'avoir "une sémantique d'entité" ??

    Mon problème à la base est simplement que lorsque j'écris :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    TData * data = new TData();
    TTypeA *typeA = new TTypeA();
    data->type = typeA;
    TDataFille *fille = new TDataFille(data);
     
    ...
     
    delete data;
    delete fille;
    je me retrouve avec un gros problème lors du delete de "data" et celui de "fille" car ils essayent tous les 2 de détruire "typeA"... Mon idée était donc de "cloner" typeA afin qu'il existe bien 2 instances et que chaque delete ai une instance à détruire...

    Pascale38

  6. #6
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par Pascale38 Voir le message
    Salut Koala01,
    hum là j'avoue que tu dépasses et de loin mes connaissances en la matière, notamment lorsque tu parles d'avoir "une sémantique d'entité" ??
    en gros:

    Il est possible de diviser l'ensemble des objets que tu vas manipuler en deux grandes catégories:

    1. Les (types d') objets ayant sémantique de valeur et
    2. Les (types d') objets ayant sémantique d'entité.

    Quand un objet a sémantique de valeur, tu peux parfaitement retrouver différentes variables utilisant des adresses mémoire différente mais présentant la même ... valeur dans ton code sans que cela ne te pose de problème.

    C'est, par exemple, le cas des types primitifs (bon, ce ne sont pas des classes ) ou de la classe string.

    On pourrait dire qu'une classe ayant sémantique de valeur est une classe pour laquelle il n'y a aucun besoin d'assurer une "unicité identitaire".

    Si tu as une classe Couleur, Position ou encore Force, Pression, Puissance etc, les valeurs qui composent ce genre de classe suffisent pour pouvoir déterminer une égalité entre différentes instances.

    Ce n'est, en effet, pas parce que tu as une couleur rouge quelque part que tu devras utiliser chaque fois la même instance de cette couleur pour la représenter

    A l'inverse, les objets ayant sémantique d'entité présentent une obligation "d'unicité identitaire", dans le sens où, même si tu arrives à trouver deux objets dont l'ensemble des caractéristiques sont identiques, il y aura toujours "quelque chose" qui permettra d'identifier une instance particulière de manière unique et non ambigüe (au minimum l'adresse mémoire à laquelle se trouve l'objet en question).

    Par exemple, tu pourrais trouver une voiture de même marque, de même modèle, de même cylindrée, avec les mêmes options et le même kilométrage (aux centaines de mètres près) que la tienne sans que ce ne soit forcément la tienne:

    Tu ne pourras dire que c'est la tienne que si elle porte effectivement la même plaque d'immatriculation et le même numéro de chassis ou, de manière plus simple encore, si... elle utilise la même adresse mémoire que la tienne

    Un autre exemple pourrait être celui d'un compte en banque : ce n'est pas parce que deux comptes en banque présentent, à un instant T, le même solde que tu peux t'amuser à les mélanger et en arriver à une situation où ton salaire serait versé sur le compte de ton voisin, ou à une situation où ton voisin effectuerait ses payements depuis le tien

    A partir de ces explications, on peut tirer un grand nombre de conclusions sur ce qui peut être fait (ou non) avec des objets ayant sémantique de valeur et / ou avec des objets ayant sémantique d'entité, mais, les trois principales sont:
    1. Qu'un objet ayant sémantique de valeur peut être copié (vu qu'on peut trouver la même valeur à plusieurs adresses mémoire en même temps) alors qu'un objet ayant sémantique d'entité ne peut pas l'être (même une copie conforme de ta voiture ne serait pas TA voiture )
    2. Qu'un objet ayant sémantique de valeur peut etre assigné alors qu'un objet ayant sémanbtique d'entité ne peut pas l'être.
    3. Que deux objets ayant sémantique de valeur peuvent être comparés (par égalité), alors qu'il n'y a aucun sens à permettre ce genre de comparaison avec les objets ayant sémantique d'entité

    Mon problème à la base est simplement que lorsque j'écris :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    TData * data = new TData();
    TTypeA *typeA = new TTypeA();
    data->type = typeA;
    TDataFille *fille = new TDataFille(data);
     
    ...
     
    delete data;
    delete fille;
    je me retrouve avec un gros problème lors du delete de "data" et celui de "fille" car ils essayent tous les 2 de détruire "typeA"... Mon idée était donc de "cloner" typeA afin qu'il existe bien 2 instances et que chaque delete ai une instance à détruire...
    Et ce serait une très mauvaise idée

    En effet, cela impliquerait que, une fois que tu aurais cloné ta donnée, les deux objets qui manipulent la variable commune pourraient la faire évoluer de manière différente, et peut etre de manière carrément incompatible entre elles...

    Lorsque tu voudrais récupérer et manipuler cette donnée, tu en arriverais donc très rapidement à une situation où, en fonction de l'objet au départ duquel tu récupères la données, tu pourrait avoir un résultat diamétralement opposé

    Si je comprends ton principe, ton idée vient, essentiellement, du fait que tu te trouves confrontée à un problème de conception majeur : tu n'as pas (encore) réfléchi à qui prend la responsabilité de la durée de vie de ta donnée

    De manière générale, il faut toujours garder en tête le fait que, quand tu travailles avec des objets polymorphes, tu dois avoir un (et un seul !!!) type d'objet qui joue le role de "fournisseur" de l'objet polymorphe (et qui prend, entre autres, en charge la gestion de sa durée de vie ) et que tous les autres types d'objet qui pourraient manipuler cet objet polymorphe endossent le role "d'utilisateur" de l'objet.

    C'est à dire qu'ils peuvent utiliser l'objet à leur plus grande convenance, mais qu'il n'ont aucun droit sur sa durée de vie.

    Il n'est pas forcément nécessaire de faire en sorte de centraliser la destruction de tous les objets polymorphes à un seul endroit, mais, en tout état de cause, il est indispensable de faire en sorte qu'il n'y ait qu'un type qui puisse décider de la destruction

    ainsi, on pourrait avoir une destruction "centralisée, sous une forme proche de
    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
    class ObjectManager // à défaut d'autre terme, meme s'il est tendancieux ;)
    {
        public:
             // toadd peut avoir été créé "par ailleurs" ;)
            void add(MyPolyMorphicBase * toadd)
            {
                items_.push_back(toadd);
            }
            void remove(MyPolyMorphicBase * torem)
            {
                 for(auto it = items_.begin();
                       it != items_.end(); ++it)
                 {
                     if((*it) == torem)
                     {
                         delete (*it);
                         items_.erase(it);
                         return;
                     }
                 }
                 throw ElementNotFound();
            }
        private:
            std::vector<MyPolyMorphicBase * > items_;
    }
    ou quelque chose de "décentralisé" (dans le sens où ce n'est pas une instance unique d'un objet qui s'occupe de la destruction) sous une forme proche de
    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
    class MyPolyMorphicBase;
     
    class MyPolyMorphicHolder
    {
        public:
            // data a été créé "par ailleurs"
            MyPolyMorphicHolder(MyPolyMorphicBase * data):data_(data){}
            ~MyPolyMorphicHolder()
            {
                delete data_;
            }
        private:
            MyPolyMorphicBase * data_;
    };
    class MyHolderClass
    {
        public:
            MyHolderClass(MyPolyMorphicBase * data):hoder_(data){}
            ~MyHolderClass()
            {
                /* héhé... on ne fait rien ici, holder_ sera automatiquement détruit
                 * avec l'objet de type MyHolderClass, ce qui appellera..
                 * le destructeur de MyPolyMorphicHolder (et donc le delete sur data_) :D
                 */
            }
        private:
            MyPolyMorphicHolder holder_;
    };
    [EDIT]Généralement, si tu en viens à envisager le fait que deux types différents puissent devoir s'occuper de la destruction d'un objet polymorphe, c'est souvent le signe qu'il est plus que temps d'envisager de créer une classe qui aura seule cette responsabilité
    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

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Une classe pour mouler des instances qui soient des classes
    Par eyquem dans le forum Général Python
    Réponses: 2
    Dernier message: 03/09/2009, 17h09
  2. Réponses: 3
    Dernier message: 05/08/2009, 10h45
  3. Réponses: 4
    Dernier message: 27/03/2008, 21h27
  4. Réponses: 2
    Dernier message: 16/05/2007, 16h13
  5. Réponses: 2
    Dernier message: 27/10/2006, 18h22

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