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 :

[C++][Qt] Projet de Développement: Conseils et bonnes pratiques


Sujet :

C++

  1. #81
    Membre confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2012
    Messages
    87
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2012
    Messages : 87
    Par défaut
    Merci pour vos contributions.

    Si vous me le permettez, je vais essayer d'exposer un peu plus précisément mon héritage actuel. Je trouve que parler de notions abstraites sur des exemples abstraits, ca commence à manquer un peu de concret
    Et avec cela, j'espère que vous pourrez me dire déjà si mon héritage a un problème, et si non, me donner quelques solutions à toutes ces abstractions.

    Je manipule des points, de la notion toute simple qu'on admet habituellement à des entités bien plus précises. En résumé, j'ai créé 6 classes de points. J'ai donc mis en place le système d'héritage dont il est question, pour matérialiser le tout, car bien entendu, toutes ces classes ont certaines similitudes.
    J'ai donc:
    Point 1.1
    /
    Point 0
    \
    Point 1.2 - Point 2.1
    \
    Point 2.2 - Point 3

    * La classe Point 0 contient les attributs de base pour un point, à savoir X, Y et Z. Celle-ci ne doit pas être abstraite, des points très simples doivent pouvoir être créés.
    * Point 1.1 hérite de Point 0, et rajoute quelques attributs basiques. Elle doit également être instanciable.
    * Point 1.2 hérite de Point 0, et correspond à la classe Mere présentée plus haut. En partant d'un point simple, elle ajoute de nombreux attributs de base, communs à ses classes filles. La classe en elle-même correspond effectivement à une notion plutôt abstraite, car il n'y a pas d'objet Point 1.2 à proprement parler, uniquement des Points 2.1 et Points 2.2.
    Cependant, comme elle hérite de Point 0, qui n'est pas abstraite, est-ce possible "d'intercaler" dans un héritage une classe abstraite au milieu d'autres qui ne le sont pas?
    * Les classes 2.1 et 2.2 sont des particularités de 1.2, en quelque sorte, 2 types de points qui se basent sur 1.2. Ce sont ces dernières qui sont le plus utilisées, instanciées, etc... et qui doivent être stockées dans une map, ensemble. Elles étaient nommées Fille1 et Fille2.
    * Finalement, la classe Point 3 est une spécialisation de Point 2.2, objet du même "type" mais avec quelques particularités. Elle doit également être instanciée, objets contenus dans une map. Elle correspond à Fille1.1 plus haut.

    Voilà donc mon héritage. Tous les attributs des classes sont des types de la STL: int, double, bool et string. Ces différentes classes sont utilisées à travers des fonctions placées dans des namespaces, hors des classes, passées le plus souvent en référence constante.

    Voyez-vous un accroc dans mon système d'héritage?
    Ou est-ce qu'a priori, il devrait pouvoir fonctionner?

    Ce qu'il faudrait donc que je fasse, c'est rendre ma classe Point 1.2 abstraite?
    Et donc d'y placer des fonctions virtuelles, sans implémentation, uniquement avec = 0 à la fin de la déclaration?
    TOUTES ces fonctions virtuelles seraient alors implémentées dans chacune des classes Point 2.1 et Point 2.2 pour renvoyer ce qu'il faut et les rendre "pas abstraites" (instanciables)?
    Tout cela n'impactant pas Point 3?
    Et pour finir il faudra également créer des destructeurs virtuels dans Point 1.2, 2.1 et 2.2?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    Point1.2.hpp
    virtual ~Point1.2();
    
    Point2.1.hpp
    virtual ~Point2.1();
    
    Point2.2.hpp
    virtual ~Point2.2();
    Et laisser leur implémentation vide, puisque seuls des types courants sont utilisés pour leurs attributs, aucun pointeur?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    Point1.2.cpp
    ~Point1.2(){}
    
    Point2.1.cpp
    ~Point2.1(){}
    
    Point2.2.cpp
    ~Point2.2(){}
    Avec cela, mon code est-il un peu plus correct?

  2. #82
    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 Gm7468 Voir le message
    Merci pour vos contributions.

    Si vous me le permettez, je vais essayer d'exposer un peu plus précisément mon héritage actuel. Je trouve que parler de notions abstraites sur des exemples abstraits, ca commence à manquer un peu de concret
    Et avec cela, j'espère que vous pourrez me dire déjà si mon héritage a un problème, et si non, me donner quelques solutions à toutes ces abstractions.

    Je manipule des points, de la notion toute simple qu'on admet habituellement à des entités bien plus précises. En résumé, j'ai créé 6 classes de points. J'ai donc mis en place le système d'héritage dont il est question, pour matérialiser le tout, car bien entendu, toutes ces classes ont certaines similitudes.
    J'ai donc:
    Point 1.1
    /
    Point 0
    \
    Point 1.2 - Point 2.1
    \
    Point 2.2 - Point 3

    * La classe Point 0 contient les attributs de base pour un point, à savoir X, Y et Z. Celle-ci ne doit pas être abstraite, des points très simples doivent pouvoir être créés.
    * Point 1.1 hérite de Point 0, et rajoute quelques attributs basiques. Elle doit également être instanciable.
    * Point 1.2 hérite de Point 0, et correspond à la classe Mere présentée plus haut. En partant d'un point simple, elle ajoute de nombreux attributs de base, communs à ses classes filles. La classe en elle-même correspond effectivement à une notion plutôt abstraite, car il n'y a pas d'objet Point 1.2 à proprement parler, uniquement des Points 2.1 et Points 2.2.
    Cependant, comme elle hérite de Point 0, qui n'est pas abstraite, est-ce possible "d'intercaler" dans un héritage une classe abstraite au milieu d'autres qui ne le sont pas?
    N'onobstant les réponses aux autres question, on peut estimer que Point 2.1 présentera des comportement supplémentaires par rapport à Point...

    Ces nouveaux comportements peuvent être déclarés virtuels purs dans Point 1.2, ce qui en fera, de facto une classe abstraite, alors que sa classe mère (Point 0) est une classe concrète
    * Les classes 2.1 et 2.2 sont des particularités de 1.2, en quelque sorte, 2 types de points qui se basent sur 1.2. Ce sont ces dernières qui sont le plus utilisées, instanciées, etc... et qui doivent être stockées dans une map, ensemble. Elles étaient nommées Fille1 et Fille2.
    Tu peux les placer aussi bien dans des collections utilisant des pointeurs sur point 1.2 que dans des collections utilisant des pointeurs sur point 0, c'est le but de l'héritage.

    Penses cependant que, si tu mets des objets de type Point 2.1 ou Point 3 dans une collection de pointeurs sur Point 1.2, tu ne pourras manipuler tes objets qu'au travèrs de l'interface publique de Point 1.2 (hors transtypage ou double dispatch)
    * Finalement, la classe Point 3 est une spécialisation de Point 2.2, objet du même "type" mais avec quelques particularités. Elle doit également être instanciée, objets contenus dans une map. Elle correspond à Fille1.1 plus haut.
    toujours aucun problème

    Voilà donc mon héritage. Tous les attributs des classes sont des types de la STL: int, double, bool et string. Ces différentes classes sont utilisées à travers des fonctions placées dans des namespaces, hors des classes, passées le plus souvent en référence constante.
    Cela semble cohérent..
    Voyez-vous un accroc dans mon système d'héritage?
    Ou est-ce qu'a priori, il devrait pouvoir fonctionner?
    Pas d'accroc en première analyse, oui, a priori, il devrait fonctionner.

    Cependant, j'ai tendance à envisager une classe point comme étant une classe ayant sémantique de valeur, ce qui n'est pas compatible avec le concept même de l'héritage

    De plus, j'ai vraiment du mal à imaginer qu'un point puisse réellement avoir besoin de plus de données que... les données qui lui permettent de se situer par rapport à d'autres point re

    Peut etre est-ce simplement que les noms sont mal choisis, mais il y a peut etre un problème de conception à ce niveau là

    Ce qu'il faudrait donc que je fasse, c'est rendre ma classe Point 1.2 abstraite?
    Et donc d'y placer des fonctions virtuelles, sans implémentation, uniquement avec = 0 à la fin de la déclaration?
    tout à fait
    TOUTES ces fonctions virtuelles seraient alors implémentées dans chacune des classes Point 2.1 et Point 2.2 pour renvoyer ce qu'il faut et les rendre "pas abstraites" (instanciables)?
    c'est bien cela
    Tout cela n'impactant pas Point 3?
    si toutes les fonctions virtuelles pures déclarées dans point 1.2 sont définies dans point 2.2, ta classe point 3 sera déjà classe concrète, tout en gardant la possibilité de redéfinir les fonctions en cas de besoin

    Et pour finir il faudra également créer des destructeurs virtuels dans Point 1.2, 2.1 et 2.2?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    Point1.2.hpp
    virtual ~Point1.2();
    
    Point2.1.hpp
    virtual ~Point2.1();
    
    Point2.2.hpp
    virtual ~Point2.2();
    C'est toujours mieux, en effet, bien que la norme accepte qu'on ne le fasse pas, dans un but essentiel de "documentation"

    Et laisser leur implémentation vide, puisque seuls des types courants sont utilisés pour leurs attributs, aucun pointeur?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    Point1.2.cpp
    ~Point1.2(){}
    
    Point2.1.cpp
    ~Point2.1(){}
    
    Point2.2.cpp
    ~Point2.2(){}
    tout à fait
    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. #83
    Membre confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2012
    Messages
    87
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2012
    Messages : 87
    Par défaut
    Bon ca fait plaisir quelques confirmations

    Citation Envoyé par koala01 Voir le message
    Penses cependant que, si tu mets des objets de type Point 2.1 ou Point 3 dans une collection de pointeurs sur Point 1.2, tu ne pourras manipuler tes objets qu'au travèrs de l'interface publique de Point 1.2 (hors transtypage ou double dispatch)
    Qu'est-ce que cela implique?
    Que les fonctions spécifiques développées dans Point 2.1 ou 2.2 ne pourront pas être utilisées?
    Cela peut-il être contourné en implémentant ces fonctions spécifiques dans Point 1.2 avec un renvoi de valeurs par défaut, et en les ré-implémentant (surcharge) dans les classes filles où c'est pertinent?

    Citation Envoyé par koala01 Voir le message
    Cependant, j'ai tendance à envisager une classe point comme étant une classe ayant sémantique de valeur, ce qui n'est pas compatible avec le concept même de l'héritage
    Effectivement, on peut plus ou moins considérer la classe comme sémantique de valeur, mais les objets points correspondent bel et bien dans mon application à une réalité physique, qui empêche que deux objets points soient égaux (tous leurs attributs égaux).

    Citation Envoyé par koala01 Voir le message
    De plus, j'ai vraiment du mal à imaginer qu'un point puisse réellement avoir besoin de plus de données que... les données qui lui permettent de se situer par rapport à d'autres point re
    Cela peut paraitre a première vue incorrect je l'avoue. Cependant, il faut voir les différents points créés comme des entités physiques, matérialisées, et auxquelles on peut associer des types, c'est-à-dire les classer dans des catégories; et non pas uniquement comme les entités mathématiques habituelles. Ainsi, tous les points ont certains caractéristiques et comportements similaires (attributs et fonctions des classes mères), mais également, suivant leur classement (type), d'autres comportements et caractéristiques différents entre eux, qui aboutissent sur des actions différentes.

    Citation Envoyé par koala01 Voir le message
    Peut etre est-ce simplement que les noms sont mal choisis, mais il y a peut etre un problème de conception à ce niveau là
    En toute modestie, je pense a priori qu'il n'y a pas de problème de conception; les noms que j'ai employés ici sont effectivements des noms fictifs, ils ne correspondent pas à mes classes. C'était encore une fois uniquement pour présenter le fonctionnement global et cibler sur le problème sans avoir à expliquer tout le mécanisme et la dynamique qu'il y a derrière.

  4. #84
    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 Gm7468 Voir le message
    Qu'est-ce que cela implique?
    Que, si tu as une fonction "faitMachinTruc" qui n'existe que dans point 3 ou dans point 2.1, tu ne pourras pas invoquer cette fonction au départ de ton pointeur (du moins, sans passer par un transtypage ou par le double dispatch ; ) )
    Que les fonctions spécifiques développées dans Point 2.1 ou 2.2 ne pourront pas être utilisées?
    toute fonction propre à la classe dérivée n'ayant aucun sens dans la classe de base

    Je ne connais pas suffisamment ton projet pour être plus précis, et c'est la raison pour laquelle je te mets en garde : lorsque tu disposes d'un pointeur sur la classe mère, tu ne peux invoquer que les fonctions publiques de la classe mère (ou des classe dont hérite la classe mère)
    Cela peut-il être contourné en implémentant ces fonctions spécifiques dans Point 1.2 avec un renvoi de valeurs par défaut, et en les ré-implémentant (surcharge) dans les classes filles où c'est pertinent?
    oui, pour autant qu'il soit, comme tu le dis si bien, pertinent de placer cette fonction au niveau de la classe mère
    Effectivement, on peut plus ou moins considérer la classe comme sémantique de valeur, mais les objets points correspondent bel et bien dans mon application à une réalité physique, qui empêche que deux objets points soient égaux (tous leurs attributs égaux).

    Cela peut paraitre a première vue incorrect je l'avoue. Cependant, il faut voir les différents points créés comme des entités physiques, matérialisées, et auxquelles on peut associer des types, c'est-à-dire les classer dans des catégories; et non pas uniquement comme les entités mathématiques habituelles. Ainsi, tous les points ont certains caractéristiques et comportements similaires (attributs et fonctions des classes mères), mais également, suivant leur classement (type), d'autres comportements et caractéristiques différents entre eux, qui aboutissent sur des actions différentes.
    c'est la raison pour laquelle j'ai pris la peine de préciser que c'étais une approche personnelle (j'aurais pu insister un peu d'avantage là dessus, je te l'accorde).
    En toute modestie, je pense a priori qu'il n'y a pas de problème de conception; les noms que j'ai employés ici sont effectivements des noms fictifs, ils ne correspondent pas à mes classes. C'était encore une fois uniquement pour présenter le fonctionnement global et cibler sur le problème sans avoir à expliquer tout le mécanisme et la dynamique qu'il y a derrière.
    Cette remarque n'était qu'une mise en garde sur base " de ce que je sais" de ton projet (ou plutot de ce que je crois en savoir), et relative aux noms que tu as utilisé dans ta question précédente.

    C'est la raison pour laquelle j'ai insisté sur le peut etre

    Il n'est donc pas du tout impossible que tu aies choisi des noms qui représentent réellement l'usage fait de tes différentes classes, mais je trouve malgré tout important de rappeler que le nom d'une classe doit, autant que possible, être en relation directe avec la sémantique que l'on donnerait à ce nom (ou plutot à l'objet représenté par le type auquel on réfléchit) "dans la vie de tous les jours".

    Cela facilite grandement les choses lorsqu'il s'agit de se poser certaines questions
    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. #85
    Membre confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2012
    Messages
    87
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2012
    Messages : 87
    Par défaut
    J'ai conscience que je reste évasif sur la plupart des points, mais n'hésitez à demander des précisions si nécessaire.
    J'avais effectivement compris que c'était ton avis personnel, mais vu la qualité de cet avis depuis le début du post, je me devais de justifier les "choix" effectués. J'en profite pour te féliciter pour tes 10 000 points, qui confirment la pertinence de tes réponses.

    Je vais essayer de mettre tout cet héritage en place, ou du moins de modifier ce qui doit l'être.

    Il ne reste donc à présent plus qu'une question:
    Citation Envoyé par Gm7468 Voir le message
    celle-ci

  6. #86
    Membre confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2012
    Messages
    87
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2012
    Messages : 87
    Par défaut
    Voilà, tout a été mis en œuvre
    J'ai fait les modifications nécessaires dans les classes, avec les fonctions virtuelles, les destructeurs, etc...
    J'ai également modifié mon code, pour que, dans les maps que j'utilisais, à la place des objets Point, je place maintenant des pointeurs vers les points.
    Le compilateur ne pose plus de problème, j'ai corrigé tous les détails qui avaient à l'être, mais cette fois, c'est l'exécution qui bloque.
    En effet, à la place des valeurs, j'ai partout des adresses, et je n'arrive pas à déréférencer les pointeurs. J'ai essayé *, j'ai mis des *(...), mais rien n'y fait, je n'accède qu'aux adresses.

    Tout d'abord, je crée mes maps, et les remplis:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    std::map<int, Point1.2*> points;
    Point2.1 premierPoint(attributs);
    Point2.2 secondPoint(attributs);
    
    points[0] = &premierPoint;
    point[1] = &secondPoint;
    Auparavant, ces étapes fonctionnaient, j'ai uniquement ajouté les références (&, alors qu'avant je plaçais directement les points dans les maps).

    Cette map points est contenue dans la structure de données donnees, pour y accéder, je fais donc:
    J'ai également une fonction sur la map qui me renvoie le pointeur vers le premier élément (équivalent de begin()). J'utilise ainsi, pour obtenir un pointeur vers le premier élément de la map:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    donnees.points.getItPointsBegin()
    Pour accéder à la seconde partie de la map:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    donnees.points.getItPointsBegin()->second
    Je suis donc à ce moment sur le pointeur vers l'objet point que j'ai placé dans la map (Point2.1 ou 2.2).
    J'utilise alors une fonction membre de Point1.2:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    donnees.points.getItPointsBegin()->second->getNumeroPoint()
    (avant les modifications et les pointeurs, j'utilisais un "." à la place du "->" pour accéder à la fonction).

    Et cela me donne à présent une adresse (du type 2279488), alors qu'avant j'accédais bien à ce que je voulais (le numéro du point, contenu dans un attribut de Point1.2, par exemple 25).

    Comment procéder pour le déréférencement?
    Comment accéder à la valeur pointée par le pointeur?

    Lorsque je mets *:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    *donnees.points.getItPointsBegin()->second->getNumeroPoint()
    Le compilateur me renvoie:
    invalid type argument of 'unary *'

  7. #87
    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 Gm7468 Voir le message
    Voilà, tout a été mis en œuvre
    J'ai fait les modifications nécessaires dans les classes, avec les fonctions virtuelles, les destructeurs, etc...
    J'ai également modifié mon code, pour que, dans les maps que j'utilisais, à la place des objets Point, je place maintenant des pointeurs vers les points.
    Le compilateur ne pose plus de problème, j'ai corrigé tous les détails qui avaient à l'être, mais cette fois, c'est l'exécution qui bloque.
    En effet, à la place des valeurs, j'ai partout des adresses, et je n'arrive pas à déréférencer les pointeurs. J'ai essayé *, j'ai mis des *(...), mais rien n'y fait, je n'accède qu'aux adresses.

    Tout d'abord, je crée mes maps, et les remplis:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    std::map<int, Point1.2*> points;
    Point2.1 premierPoint(attributs);
    Point2.2 secondPoint(attributs);
    
    points[0] = &premierPoint;
    point[1] = &secondPoint;
    Il y a un énorme danger à travailler de la sorte!!!


    En effet, je présumes que cela intervient dans une fonction (voir dans une fonction membre de ta classe "données") et non dans la fonction principale, 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
     
    std::map<int, Point1.2*> initMap()
    {
        std::map<int, Point1.2*> points;
        Point2.1 premierPoint(/*attributs*/);
        Point2.2 secondPoint(/*attributs*/);
     
        points[0] = &premierPoint;
        point[1] = &secondPoint;
    } // CRAC : premierPoint et secondPoint sont détruits ici
      // les pointeurs de ta map deviennent donc invalides
    Le problème (cf le commentaire que j'ai placé en fin de fonction), c'est que ... premierPoint et secondPoint sont détruits en fin de fonction :-$

    Avec comme résultat le fait que, quand tu essayes d'accéder à tes points, 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
     
    void machinChose(std::map<int, Point1.2*> /* const */ & lamap)
    {
        /* que l'on travaille avec l'itérateur ou avec l'opérateur [] ne changera
         * pas grand chose (modulo le comportement particulier de l'opérateur [] )
         */
        Point1.2 * point = lamap[0]; // OUUUPPSSS... on récupère un pointeur qui...
                                     // ne pointe en tout cas pas sur un Point1.2 valide :-$
        point->doSomething(); // BOUM le pointeur ne pointant sur rien de valide,
                              // on a une erreur de segmentation
    }
    Le pointeur contient une adresse qui, en tout état de cause, ne donne pas accès à un objet de type Point1.2.

    Dans ce cas, le comportement est indéfini et tu pourrais très bien, par mégarde, lancer la troisième guerre mondiale en envoyant "sur le réseau" un information qui lancerait les missiles balistiques US
    Auparavant, ces étapes fonctionnaient, j'ai uniquement ajouté les références (&, alors qu'avant je plaçais directement les points dans les maps).
    Sauf que le &, dans le cas présent, n'est pas le symbole d'une référence, mais... l'opérateur "addressOf" qui récupère... l'adresse à laquelle se trouve l'objet.

    Et c'est, quelque part, tout à fait normal que tu doive utiliser cet opérateur car tu as défini ta map comme prenant un entier en clé et un pointeur sur un objet de type Point1.2 comme valeur.

    Seulement, comme l'objet en question cesse d'exister quand tu sort de la fonction d'initialisation, le pointeur ne pointe plus sur rien de valide

    Tu as donc deux solutions :
    1. Soit tu abandonnes l'idée d'utiliser des pointeurs pour la valeur de ta map
    2. Soit tu n'auras pas le choix : tu devras passer par l'allocation dynamique de la mémoire (new)
    Seulement, de ces deux solutions, il y en a une qui est inacceptable

    En effet, le fait d'utiliser des valeurs et non des pointeurs t'oblige à faire en sorte que Point1.2 soit copiable (note que c'est ce que prévoit le compilateur par défaut (comprend : tant qu'on ne lui a pas donné de raison de faire autrement) ), MAIS...

    Ta classe Point1.2 intervient dans une hiérarchie de classes (elle hérite d'une autre et est dérivée en deux autres classes), ce qui fait qu'elle a, de facto, sémantique d'entité, et la copie est incompatible avec une sémantique d'entité

    La preuve en est que ta classe Point1.2 est dérivée en Point 2.1, en Point 2.2 et en Point 3, qui, je te site :
    * Point 1.2 hérite de Point 0, et correspond à la classe Mere présentée plus haut. En partant d'un point simple, elle ajoute de nombreux attributs de base, communs à ses classes filles. La classe en elle-même correspond effectivement à une notion plutôt abstraite, car il n'y a pas d'objet Point 1.2 à proprement parler, uniquement des Points 2.1 et Points 2.2.
    <snip>
    * Les classes 2.1 et 2.2 sont des particularités de 1.2, en quelque sorte, 2 types de points qui se basent sur 1.2. Ce sont ces dernières qui sont le plus utilisées, instanciées, etc... et qui doivent être stockées dans une map, ensemble. Elles étaient nommées Fille1 et Fille2.
    * Finalement, la classe Point 3 est une spécialisation de Point 2.2, objet du même "type" mais avec quelques particularités. Elle doit également être instanciée, objets contenus dans une map. Elle correspond à Fille1.1 plus haut.
    Le fait de faire "entrer de force" des objets qui pourraient tout aussi bien être des Point 2.1, des Point 2.2 ou des Point 3 dans une map dont les valeur seraient... des copie des point ferait que tu perdrait toutes les informations relatives aux types réels de tes objets (respectivement Point 2.1, Point 2.2 et Point 3).

    Tu comprends surement pourquoi je me suis écrié que cette solution est inacceptable, non

    Tu n'as donc pas d'autre solution : tu dois utiliser l'allocation dynamique de la mémoire
    Cette map points est contenue dans la structure de données donnees, pour y accéder, je fais donc:
    Cela tend à confirmer l'assertion que j'ai faite en estimant que ta map était remplie dans une autre fonction que celle dans laquelle son contenu est utilisé
    J'ai également une fonction sur la map qui me renvoie le pointeur vers le premier élément (équivalent de begin()). J'utilise ainsi, pour obtenir un pointeur vers le premier élément de la map:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    donnees.points.getItPointsBegin()
    Pour accéder à la seconde partie de la map:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    donnees.points.getItPointsBegin()->second
    Je suis donc à ce moment sur le pointeur vers l'objet point que j'ai placé dans la map (Point2.1 ou 2.2).
    [/QUOTE]Justement, non!!!

    Comme l'objet en question a été détruit de manière automatique à la fin de la fonction qui remplissait la map, ton pointeur pointe sur un adresse où l'on "pouvait trouver, jadis" un objet de type Point2.1 ou de type Point2.2.

    Mais, l'objet ayant été détruit, on y trouve, maintenant "les crasses d'une utilisation antérieur" voire, carrément autre chose

    Tous les problèmes que tu décris par la suite sont dus exclusivement à ce phénomène

    Tu dois donc, comme je l'ai dit plus haut, veiller à ce que les objets continuent à exister une fois que l'on sort de la fonction qui les crée, et, pour cela, seule l'allocation dynamique pourra te sauver

    Cependant, cela ne te sauvera que temporairement, dans le sens où il s'agira de veiller à libérer correctement la mémoire une fois que tu n'as plus besoin de l'un (ou de tous) des objets créé de manière dynamique.

    Il t'appartient donc de réfléchir à "qui" ou "quoi" peut prendre la responsabilité de la destruction de ces objets

    L'idéal serait, peut etre, de se tourner vers les smart pointers : des pointeurs suffisamment intelligents pour décider de libérer la mémoire "en temps utiles"
    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

  8. #88
    Membre confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2012
    Messages
    87
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2012
    Messages : 87
    Par défaut
    Tout s'explique!
    Encore un petit accroc dans le programme, ce n'est pas le premier, ca ne sera pas le dernier, mais comme vous êtes là pour m’aiguiller, tout va bien

    Je faisais donc... n'importe quoi, encore une fois. J'espère que je n'ai pas fait exploser de maison...

    Trêve de plaisanterie, effectivement, les maps sont remplies dans une fonction d'initialisation. Il faut donc que je passe par l'allocation dynamique pour cette initialisation, soit:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    std::map<int, Point1.2*> points;
    Point1.2 *pointeur(0);
     
    pointeur = new Point2.1(/*attributs*/);
    points[0] = pointeur;
     
    pointeur = new Point2.2(/*attributs*/);
    points[1] = pointeur;
    C'est plus correct?

    Et après je peux effectuer les utilisations comme je les ai faites?
    Puisque cette fois, c'est bien un pointeur que j'obtiens, vers un objet Point2.1 ou 2.2?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    donnees.points.getItPointsBegin()->second->getNumeroPoint()
    Pour la libération de la mémoire...
    Les maps de points sont contenues dans un objet qui est placé dans la structure de données:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    struct Structure{
    (...)
    Objet objet;
    }
     
    class Objet{
    (...)
    private:
        std::map<int, Point1.2*> mapUne;
        std::map<int, Point3*> mapDeux;
    }
    Ces maps sont donc initialisées dans une fonction initialise() quelque part, et sont conservées tout au long du programme, jusqu'à la fin.

    Je suppose que ca serait trop simple (et donc faux) de faire confiance au destructeur par défaut de la structure qui appelle le destructeur par défaut de la classe qui détruirait les maps? Ou même de faire un clear() sur les maps?
    Je dois donc déclarer et implémenter des destructeurs dans ces fichiers?
    Est-ce la solution (ou une solution... juste):

    Structure.hpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    struct Structure{
    /*membres*/
    }
    //destructeur
    ~Structure(){} //vide
    Objet.hpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    class Objet{
    //destructeur
    ~Objet();
    }
    Objet.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    ~Objet(){
    for(std::map<int, Point1.2*>::iterator itUn = mapUne.begin(); itUn != mapUne.end(); itUn++){
    delete itUn; //ou *itUn?
    }
    for(std::map<int, Point3*>::iterator itDeux = mapDeux.begin(); itDeux != mapDeux.end(); itDeux++){
    delete itDeux; //ou *itDeux?
    }
    }
    Seule la destruction des pointeurs est nécessaire?
    Celle-ci appelant le destructeur des objets eux-mêmes, et comme les objets eux-mêmes ne contiennent que des membres de type standards, un destructeur vide ou par défaut suffit?

  9. #89
    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 Gm7468 Voir le message
    Tout s'explique!
    Encore un petit accroc dans le programme, ce n'est pas le premier, ca ne sera pas le dernier, mais comme vous êtes là pour m’aiguiller, tout va bien

    Je faisais donc... n'importe quoi, encore une fois. J'espère que je n'ai pas fait exploser de maison...

    Trêve de plaisanterie, effectivement, les maps sont remplies dans une fonction d'initialisation. Il faut donc que je passe par l'allocation dynamique pour cette initialisation, soit:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    std::map<int, Point1.2*> points;
    Point1.2 *pointeur(0);
     
    pointeur = new Point2.1(/*attributs*/);
    points[0] = pointeur;
     
    pointeur = new Point2.2(/*attributs*/);
    points[1] = pointeur;
    C'est plus correct?
    Beaucoup

    Et après je peux effectuer les utilisations comme je les ai faites?
    Puisque cette fois, c'est bien un pointeur que j'obtiens, vers un objet Point2.1 ou 2.2?
    Tout à fait
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    donnees.points.getItPointsBegin()->second->getNumeroPoint()
    Pour la libération de la mémoire...
    Les maps de points sont contenues dans un objet qui est placé dans la structure de donné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
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1234567891011
    struct Structure{
    (...)
    Objet objet;
    }
     
    class Objet{
    (...)
    private:
        std::map<int, Point1.2*> mapUne;
        std::map<int, Point3*> mapDeux;
    }
    Ces maps sont donc initialisées dans une fonction initialise() quelque part, et sont conservées tout au long du programme, jusqu'à la fin. :question: Je suppose que ca serait trop simple (et donc faux) de faire confiance au destructeur par défaut de la structure qui appelle le destructeur par défaut de la classe qui détruirait les maps? Ou même de faire un clear() sur les maps?
    En effet, tu viderais ta map, mais tu ne détruirais pas les objets pointés
    Je dois donc déclarer et implémenter des destructeurs dans ces fichiers?
    ouaip
    Est-ce la solution (ou une solution... juste):

    Structure.hpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    struct Structure{
    /*membres*/
    }
    //destructeur
    ~Structure(){} //vide
    Objet.hpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    class Objet{
    //destructeur
    ~Objet();
    }
    Objet.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    ~Objet(){
    for(std::map<int, Point1.2*>::iterator itUn = mapUne.begin(); itUn != mapUne.end(); itUn++){
    delete itUn; //ou *itUn?
    }
    for(std::map<int, Point3*>::iterator itDeux = mapDeux.begin(); itDeux != mapDeux.end(); itDeux++){
    delete itDeux; //ou *itDeux?
    }
    }
    Presque...

    Un iterateur fonctionne, grosso modo, comme un pointeur, ce qui fait que itUn et itDeux sont, dans le cas présent, équivalent à Point 2.1 ** (un pointeur sur un pointeur vers un objet de type "passant pour etre" de type Point 2.1...

    C'est donc bel et bien
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    ~Objet(){
    for(std::map<int, Point1.2*>::iterator itUn = mapUne.begin(); itUn != mapUne.end(); itUn++){
    delete * itUn; 
    }
    for(std::map<int, Point3*>::iterator itDeux = mapDeux.begin(); itDeux != mapDeux.end(); itDeux++){
    delete *itDeux; 
    }
    qu'il faut écrire
    Seule la destruction des pointeurs est nécessaire?
    Ouaip
    Celle-ci appelant le destructeur des objets eux-mêmes, et comme les objets eux-mêmes ne contiennent que des membres de type standards, un destructeur vide ou par défaut suffit?
    ouaip
    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

  10. #90
    Membre confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2012
    Messages
    87
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2012
    Messages : 87
    Par défaut
    Merci

    Me revoilà déjà pour un petit problème. J'ai effectué les modifications nécessaires, et toute la partie allocation dynamique a l'air de fonctionner:
    je retrouve les mêmes résultats qu'avant que je fasse toutes les modifs avec les maps et pointeurs, mais avec un fonctionnement plus "juste".

    Par contre, pour la destruction, quand j'implémente le destructeur de l'objet, le compilateur me renvoie un problème.
    Le destructeur:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    Objet::~Objet()
    {
        for(std::map<int, Point1.2*>::iterator itPoints = points.begin(); itPoints != points.end(); itPoints++){
            delete *itPoints;
        }
        for(std::map<int, Point3*>::iterator itRe = re.begin(); itRe != re.end(); itRe++){
            delete *itRe;
        }
    }
    Réponse du compilateur: (aux 2 lignes des delete)
    erreur : type 'struct std::pair<const int, Point1.2*>' argument given to 'delete', expected pointer

    J'ai cherché sur d'autres forums et il semblerait qu'une boucle while sur l'itérateur, du début, et tant qu'on arrive pas à la fin, avec un
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    iterateur = map.erase(iterateur);
    fonctionne.

    Est-ce une solution à adopter, ou est-ce qu'il y a un autre moyen pour le destructeur avec un delete qui marche?

  11. #91
    Membre émérite

    Profil pro
    Inscrit en
    Décembre 2008
    Messages
    533
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2008
    Messages : 533
    Par défaut
    std::map::begin() et end() n'itèrent pas sur les valeurs elles-mêmes, mais sur des std::pair clé-valeur. Donc :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    Objet::~Objet()
    {
        for(std::map<int, Point1.2*>::iterator itPoints = points.begin(); itPoints != points.end(); itPoints++){
            delete itPoints->second;
        }
        for(std::map<int, Point3*>::iterator itRe = re.begin(); itRe != re.end(); itRe++){
            delete itPoints->second;
        }
    }

  12. #92
    Membre confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2012
    Messages
    87
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2012
    Messages : 87
    Par défaut
    Le compilateur est d'accord avec cob59, il ne donne plus d'erreur.
    Par contre, c'est l'ordinateur qui n'aime pas le delete. Lorsque j'exécute le programme, arrivé au destructeur, il entre dans la première boucle for (donc il trouve bien un contenu dans la map), et à la première instruction delete itPoints->second; le programme s'arrête (Le programme s'est terminé subitement avec code -1073741819 pour QtCreator, et Windows affiche sa fenêtre bien connue d'erreur de programme avec la possibilité d'envoyer un rapport d'erreur...).

    Quel pourrait être le problème?

  13. #93
    Membre émérite

    Profil pro
    Inscrit en
    Décembre 2008
    Messages
    533
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2008
    Messages : 533
    Par défaut
    Est-ce que le pointeur Point1.2* qu'il extrait n'est pas NULL ou n'est pas déjà passé dans un delete ?

    Essaie d'appeler une méthode sur itPoints->second avant de le détruire, de préférence une méthode censée modifier son état. Si cet appel fait planter, c'est que le pointeur n'est pas/plus bon.

  14. #94
    Membre confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2012
    Messages
    87
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2012
    Messages : 87
    Par défaut
    Alors:
    - est-il déjà passé dans un delete?
    A priori non, en tout cas aucune chance que ce soit un delete que j'ai placé moi, car je viens à peine d'ajouter ceux-ci, et ce sont les seuls.

    - pointeur NULL?
    A priori, non plus. J'ai fait afficher la valeur du pointeur avant la ligne du delete, en 2 essais j'obtiens 0x7ffd6000 et 0x7ffd7000. Je dirais donc que le pointeur pointe bien vers une adresse.

    - méthode sur itPoints->second->getXPoint()
    Accesseur de l'attribut X. Renvoie une valeur, certes étrange (1.11918e-307), mais une valeur tout de même. Elle n'est peut être pas manipulée dans le programme, peut être mise à 0... en tout cas pas de problème là dessus.

    - méthode sur itPoints->second->setXPoint(123.23)
    Mutateur de l'attribut X. Pas de problème lors de l'exécution de la ligne, et si je refais un get après le set, le get renvoie bien 123.23.

    Mais arrivé à la ligne du delete, toujours le programme qui coupe.

  15. #95
    Membre confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2012
    Messages
    87
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2012
    Messages : 87
    Par défaut
    Bon, j'avance un petit peu:

    Dans mon objet Objet, j'ai donc 2 maps:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    std::map<int, Point1.2*> points; //dans laquelle je place des objets Point2.1 et 2.2, la classe Point1.2 étant abstraite
    std::map<int, Point3*> re;
    (cf. arborescence de l'héritage plus haut)

    Comme Windows coupait le programme - sans raisons apparente - au niveau du destructeur, j'ai placé des cout pour contrôler l'avancement (c'est probablement une technique de débug assez archaïque, mais elle a l'air de fonctionner):
    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
    Objet::~Objet()
    {
        std::cout << "destructeur" << std::endl;
        std::cout << "1" << std::endl;
        for(std::map<int, Point1.2*>::iterator itPoints = points.begin(); itPoints != points.end(); itPoints++){
            std::cout << ".i" << std::endl;
            std::cout << itPoints->second << std::endl;
            std::cout << ".k" << std::endl;
            std::cout << "get x point " << itPoints->second->getXPoint() << std::endl;
            itPoints->second->setXPoint(123.23);
            std::cout << "set 123.23 puis get x point " << itPoints->second->getXPoint() << std::endl;
            delete itPoints->second;
        }
        std::cout << "2" << std::endl;
        for(std::map<int, Point3*>::iterator itRe = re.begin(); itRe != re.end(); itRe++){
            std::cout << ".j" << std::endl;
            delete itRe->second;
        }
    }
    Et donc à l'exécution, le programme coupe après la première série de cout, donc dès le premier delete atteint, alors que l'objet pointé existe bel et bien et est valide (puisque les accesseurs/mutateurs fonctionnent).

    J'ai alors cherché du coté des destructeurs, que je venais d'implémenter dans les classes Point1.2, 2.1 et 2.2:
    Point1.2.hpp
    Point1.2.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Point1.2::~Point1.2()
    {
        std::cout << "destructeur Point1.2" << std::endl;
    }
    Point2.1.hpp
    Point2.1.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Point2.1::~Point2.1()
    {
        std::cout << "destructeur Point2.1" << std::endl;
    }
    Point2.2.hpp
    Point2.2.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Point2.2::~Point2.2()
    {
        std::cout << "destructeur Point2.2" << std::endl;
    }
    (rien dans Point3.hpp et .cpp, j'ai laissé le destructeur par défaut sans rien déclarer ni implémenter).

    A l'exécution cette fois, on a toujours le même affichage, ce qui indique que c'est directement à l'appel du destructeur (de Point2.1 ou Point2.2) que le problème se pose.

    J'ai alors décidé, dans un excès de confiance, pas si excessif que ca, de commenter toute la boucle sur la map de Point1.2* (qui contient donc des Point2.1 et 2.2). Et là, à l'exécution, plus de problème, la boucle est bien parcourue 3 fois pour mes 3 Point3 contenus dans la seconde map, et le programme termine correctement après le destructeur, et la console affiche 3 fois:
    .j
    destructeur Point2.2
    destructeur Point1.2

    Donc lors du delete sur un objet Point3, on appelle le destructeur de son parent Point2.2, puis le destructeur de son parent à lui, Point1.2 (ce qui parait correct).

    Cela implique que le destructeur de Point3 (par défaut, rien déclaré ni implémenté moi même) fonctionne, mais a priori également les destructeurs de Point2.2 et Point1.2 puisqu'ils ont été parcourus sans problème...

    Pour le moment, dans la map de Point1.2, je plaçais 1 objet Point2.2 puis un objet Point2.1. C'était pour mes tests. J'ai donc testé, en ne plaçant que 2 objets Point2.2. Et là, surprise, le programme fonctionne encore, à la destruction de la première map (qui a été décommentée), la console affiche 2 fois la série de cout puis:
    destructeur Point2.2
    destructeur Point1.2


    Ben donc c'est le destructeur de Point2.1 qui pose problème?

    Fort des différents tests, je continue: puisque 2 Point2.2 dans la map de Point1.2 fonctionnent, testons 2 Point2.1!
    Et là, c'est le drame, enfin... pas tant que ca, puisque le programme coupe à nouveau, ce qui vient - a priori - confirmer que c'est bien le destructeur de Point2.1 qui a un problème.
    Je dis a priori, car lorsque j'avais un Point2.2 et un Point2.1 dans la map, le Point2.2 était avant le Point2.1, donc il aurait du être détruit avant et donc ne pas poser de problème... M'enfin bon, il y a déjà de fortes chances qu'un des problèmes soit le destructeur de Point2.1.

    Mais alors, quelle pourrait être la cause de ce problème?
    Puisque les seules différences entre ces deux classes sont les attributs et qu'une a une fille et pas l'autre?
    Autrement, tout est similaire, elles héritent de Point1.2, ont un destructeur virtuel, ...?

    J'espère qu'avec cela vous pourrez m'aider

  16. #96
    Membre confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2012
    Messages
    87
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2012
    Messages : 87
    Par défaut
    Pour plus de clarté, si ca peut vous aider à la résolution:
    Point1.2.hpp
    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
    47
    48
    49
    50
    51
    52
    53
    54
    55
    #ifndef POINT1.2_H_INCLUDED
    #define POINT1.2_H_INCLUDED
     
    #include <iostream>
     
    #include "Point0.hpp"
    #include <string>
     
    using namespace std;
     
    /*déclaration de la classe*/
    class Point1.2 : public Point0
    {
        public:
     
        //constructeurs:
            //par défaut avec tous les attributs à zéro
            Point1.2();
            //en passant tous les attributs
            Point1.2(/*attributs...*/);
     
        //destructeur
            virtual ~Point1.2();
     
        //accesseurs et mutateurs des attributs
     
            int get() const;
     
    	/*...*/
     
            void set(int const entier);
     
    	/*...*/
     
        //fonctions virtuelles pour la gestion des classes filles
     
            virtual double getattribut1() const = 0;
            virtual double getattribut2() const = 0;
     
    	/*...*/
     
            virtual void setattribut1(double const attribut1) = 0;
            virtual void setattribut2(double const attribut2) = 0;
     
    	/*...*/
     
        protected:
     
        //attributs
            int attribut;
            /*attributs int, double, bool, string*/
     
    };
     
    #endif
    Point1.2.cpp
    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
    #include "Point1.2.hpp"
     
    //constructeurs
     
    Point1.2::Point1.2() : /*liste d'initialisation par défaut*/
    {
    }
     
    Point1.2::Point1.2(/*attributs*/) : /*liste d'initialisation*/
    {
    }
     
    //destructeur
    Point1.2::~Point1.2()
    {
        std::cout << "destructeur Point1.2" << std::endl;
    }
     
    //accesseurs
     
    int Point1.2::get() const
    {
        return attribut;
    }
     
    void Point1.2::set(int const entier)
    {
        attribut = entier;
    }
    Point2.1.hpp
    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
    #ifndef POINT2.1_H_INCLUDED
    #define POINT2.1_H_INCLUDED
     
    #include <iostream>
     
    #include "Point1.2.hpp"
     
    /*déclaration de la classe*/
    class Point2.1 : public Point1.2
    {
        public:
     
        //constructeurs:
            //par défaut avec tous les attributs à zéro
            Point2.1();
            //en passant tous les attributs
            Point2.1(/*attributs*/);
     
        //destructeur
            virtual ~Point2.1();
     
        //accesseurs et mutateurs des attributs
     
            virtual double getattribut1() const;
            virtual double getattribut2() const;
     
            virtual void setattribut1(double const attribut1);
            virtual void setattribut2(double const attribut2);
     
     
        protected:
     
        //attributs
     
            double att1;
    	/*...*/
     
    };
     
    #endif
    Point2.1.cpp
    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
    #include "Point2.1.hpp"
     
    //constructeurs
     
    Point2.1::Point2.1() : Point1.2(), /*liste d'initialisation par défaut*/
    {
    }
     
    Point2.1::Point2.1(/*attributs*/) : Point1.2(/*attributs*/), /*initialisation*/
    {
    }
     
    //destructeur
    Point2.1:~Point2.1()
    {
        std::cout << "destructeur Point2.1" << std::endl;
    }
     
    //accesseurs
     
    double Point2.1::getattribut1() const
    {
        return att1;
    }
     
    double Point2.1::getattribut2() const
    {
        return 0.0;
    }
     
    void Point2.1::setattribut1(double const attribut1)
    {
    	att1 = attribut1;
    }
     
    void Point2.1::setattribut2(double const attribut2)
    {
    }
    Point2.2.hpp
    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
    #ifndef POINT2.2_H_INCLUDED
    #define POINT2.2_H_INCLUDED
     
    #include <iostream>
     
    #include "Point1.2.hpp"
     
    /*déclaration de la classe*/
    class Point2.2 : public Point1.2
    {
        public:
     
        //constructeurs:
            //par défaut avec tous les attributs à zéro
            Point2.2();
            //en passant tous les attributs
            Point2.2(/*attributs*/);
     
        //destructeur
            virtual ~Point2.2();
     
        //accesseurs et mutateurs des attributs
     
            virtual double getattribut1() const;
            virtual double getattribut2() const;
    	/*...*/
     
            virtual void setattribut1(double const attribut1);
            virtual void setattribut2(double const attribut2);
    	/*...*/
     
        protected:
     
        //attributs
     
            double att2;
            /*...*/
     
    };
     
    #endif
    Point2.2.cpp
    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
    #include "Point2.2.hpp"
     
    //constructeurs
     
    Point2.2::Point2.2() : Point1.2(), /*liste d'initialisation par défaut*/
    {
    }
     
    Point2.2::Point2.2(/*attributs*/) : Point1.2(/*attributs*/), /*liste d'initialisation*/
    {
    }
     
    //destructeur
    Point2.2::~Point2.2()
    {
        std::cout << "destructeur Point2.2" << std::endl;
    }
     
    //accesseurs
     
    double Point2.2::getattribut1() const
    {
        return 0.0;
    }
     
    double Point2.2::getattribut2() const
    {
        return att2;
    }
     
    void Point2.2::setattribut1(double const attribut1)
    {
    }
     
    void Point2.2::setattribut2(double const attribut2)
    {
        att2 = attribut2;
    }

  17. #97
    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
    En sommes...

    Est ce que les pointeurs sur Point3 que l'on trouve dans re ne se retrouvent pas, par le plus grand des hasards, également dans ta première map

    Si tel est le cas, il ne faut pas s'étonner que ca plante lorsque tu essayes de libérer la mémoire allouée au pointeurs qui se trouvent dans la deuxième, vu qu'elle a déjà été libérée quand on est passé sur les pointeurs ... lors de la libération de la mémoire de la première.

    L'un dans l'autre, il serait sans doute beaucoup plus utile de libérer la mémoire des pointeurs les plus génériques (Point 2.1 * ) que de libérer la mémoire des pointeurs particuliers, (Point 3 * ) pour éviter qu'un point2.1 ou dérivé qui n'est pas de type Point 3 ne soit pas détruit
    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

  18. #98
    Membre confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2012
    Messages
    87
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2012
    Messages : 87
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Est ce que les pointeurs sur Point3 que l'on trouve dans re ne se retrouvent pas, par le plus grand des hasards, également dans ta première map
    Je dirais: aucune chance. Les 2 maps sont manipulées séparément, dans des boucles différentes.

    Citation Envoyé par koala01 Voir le message
    Si tel est le cas, il ne faut pas s'étonner que ca plante lorsque tu essayes de libérer la mémoire allouée au pointeurs qui se trouvent dans la deuxième, vu qu'elle a déjà été libérée quand on est passé sur les pointeurs ... lors de la libération de la mémoire de la première.

    L'un dans l'autre, il serait sans doute beaucoup plus utile de libérer la mémoire des pointeurs les plus génériques (Point 2.1 * ) que de libérer la mémoire des pointeurs particuliers, (Point 3 * ) pour éviter qu'un point2.1 ou dérivé qui n'est pas de type Point 3 ne soit pas détruit
    Je me suis peut-être mal exprimé, mais justement non. C'est la libération de la première map (de Point1.2, points) qui pose problème, alors que celle de la seconde (de Point3, re) fonctionne.
    Si je commence par libérer re puis points, la libération de re fonctionne mais pas celle de points. Si je fais dans l'ordre inverse, ca plante dès la libération de points, sans être allé jusqu'à celle de re.

    De manière programmatique, je pense être assuré que dans points, je n'ai QUE des Point2.1 et Point2.2, et dans re, QUE des Points3.

  19. #99
    Membre confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2012
    Messages
    87
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2012
    Messages : 87
    Par défaut
    Le fait que les Point2.2 fonctionnaient et pas les Point2.1 m'a incité à regarder les manipulations que je fais sur la map de Point1.2 plus en détail.
    J'ai en effet à un endroit dans le code une structure conditionnelle. Si l'attribut type issu de Point1.2, qui indique si c'est un Point2.1 ou un Point2.2, renvoie vers un Point2.1, je fais une manipulation supplémentaire, si c'est un Point2.2, je ne la fais pas.

    Ainsi, j'ai commenté cette manipulation, et cette fois plus de problème. Que la map contienne des Point2.1 ou Point2.2, le programme se termine correctement, les destructeurs fonctionnent, aussi bien pour chacune des classes (Point1.2, 2.1, 2.2, 3).

    Je vais donc creuser plus en détail dans cette manipulation, où j'utilise des pointeurs. Je dois mal replacer un pointeur ou faire une bêtise et effectivement, comme l'avaient pensé koala01 et cob59, le pointeur ne doit plus pointé vers la bonne chose...
    Ah ces fameux pointeurs... ^^
    Je vous tiens au courant

  20. #100
    Membre confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2012
    Messages
    87
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2012
    Messages : 87
    Par défaut
    J'ai identifié la ligne qui posait problème. Je faisais effectivement une mauvaise manipulation je pense. Je l'ai placée "où il fallait", et ca a l'air de fonctionner, en tout cas pour cette partie...

    Une question à présent au sujet des maps. Je rencontre un problème que je n'arrive pas à comprendre, j'ai une idée mais c'est à confirmer.

    Pour remplir mes maps, je fais:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    int increment; //qui sera manipulé et dans lequel on mettra une valeur
    Point2.1 point; //ou Point2.2
    points[increment] = point;
    Je remplis donc ma map petit à petit avec des points. L'increment est une variable qui dépend d'un des attributs des points. Donc avant la dernière ligne, je fais par exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    increment = point.getAttributIncrement();
    Par exemple, pour différents points, j'ai ces valeurs d'incrément:
    2
    6
    1


    Je place donc dans la map des points avec comme première clé ces valeurs. Les points se classent donc suivant ces valeurs (1,2,6) dans la map, tout va bien.

    Par contre, j'ai fait une fonction pour trouver l'incrément maximal d'une map. J'ai cherché dans la doc mais je n'ai rien trouvé pour le faire, il me semble qu'une telle fonction n'existe pas nativement. Je l'ai donc faite:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    int Objet::getIntPointsMax()
    {
        return points.end()->first;
    }
    Si je ne m'abuse, points.end() donne un pointeur vers le dernier élément de la map, et donc ->first la valeur de la première clé.
    Par conséquent, le dernier élément, c'est celui qui a été mis dans la map avec le plus grand increment, et la valeur de cette clé, c'est increment.
    Hors, je trouve 2 si je lance cette fonction. 2, qui correspond au nombre d'éléments dans la map - 1, ou, plus précisément, à la "position" du dernier élément (de 0 à 2 pour 3 éléments).

    Cela m'amène donc à la question qui me trotte depuis le début:
    est-ce que les premières clés que l'on place dans une map sont "remplacées" par la position dès lors qu'on renseigne la map?

    c'est-à-dire: si on fait:
    points[2] = point;
    points[6] = point;
    points[1] = point;


    les "cases" de la map sont-elles nommées {1;2;6} ou {0;1;2}?
    D'après le fonctionnement des maps, je dirais {1;2;6}, mais comment expliquer alors que ma fonction renvoie 0, 1, 2?

    Je peux bien entendu contourner le problème en faisant points.end()->second->getAttributIncrement(), mais je pensais que la map me permettait de faire autrement...

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

Discussions similaires

  1. Réponses: 0
    Dernier message: 15/07/2014, 21h31
  2. Réponses: 3
    Dernier message: 29/06/2012, 23h03
  3. Cherche conseil Certification + bonnes pratiques
    Par babass77 dans le forum Certifications
    Réponses: 3
    Dernier message: 09/02/2009, 17h42
  4. Bonne pratique, configuration de projets
    Par gailuris dans le forum EDI et Outils pour Java
    Réponses: 7
    Dernier message: 15/12/2005, 10h57

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