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

Langage C++ Discussion :

Table de hachage unordered_map et pointeurs vers objet ou objet, efficacité ?


Sujet :

Langage C++

  1. #1
    Nouveau Candidat au Club
    Homme Profil pro
    Développeur Programmation Fonctionelle
    Inscrit en
    Août 2019
    Messages
    9
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Programmation Fonctionelle
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2019
    Messages : 9
    Points : 0
    Points
    0
    Par défaut Table de hachage unordered_map et pointeurs vers objet ou objet, efficacité ?
    Bonjour,

    j'ai une classe Point3D<T> définie ainsi:

    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
    template <class T> class Point3D {
     
    public:
     
      T x,y,z; // coords 
     
     
      Point3D();
     
      ~Point3D();
     
      Point3D(T x,T y,T z);
     
      // copy constructor
      Point3D(const Point3D<T> &);
     
      // assignation operator
      Point3D<T> & operator=(const Point3D<T> &);
     
      // equality operator
      bool operator== (const Point3D<T> &);
     
    };
     
    #include "Point3D.cpp"

    Point3D.cpp contient entre autres ceci pour utilisation avec des unordered_map:

    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
    ...
     
     
    /* we need to specialize the hash function for our class
       because standart hash function works only with basic types
       such as string,int,etc...
    */
     
    // hash function for Point3D <-> Pixel
     
    template <class T> struct hash_point3d {
      size_t operator()(const Point3D<T> &p3d ) const
      {
        return hash<const Point3D<T> *>()(&p3d); // hash code is made with address !!!
      }
    };
     
     
    // equality test, mainly used with hash tables
    template <class T> struct point3DEquals : binary_function<const Point3D<T>&, const Point3D<T>&, bool> {
      bool operator()(  const Point3D<T>& lhs, const Point3D<T>& rhs ) const
      {
        return (&lhs == &rhs); // i compare the addresses !!!
      }  
    };     
     
    // equality operator
    template <class T> bool Point3D<T>::operator== (const Point3D<T> &p3d)  {
     
      return (x==p3d.x) && (y==p3d.y) && (z==p3d.z);
     
    }
     
    ...

    j'ai aussi une classe Point2D et je veux associer des Point3D projetés sur un plan en Point2D liés entre eux par une table de hachage, unordred_map pour commencer:

    je peux donc écrire cette définition :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    unordered_map<Point3D<float>,Point2D<int>,hash_point3d<float>,point3DEquals<float>> htPointPixel;
    et ça marche, mon code compile et fonctionne mais je me demande si j'aurai pas intérêt, dans un pur souci d'efficacité à définir ma table de hachage comme ceci:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    unordered_map<Point3D<float> *,Point2D<int> *> htPointersPointPixel;
    j'ai l'impression que à l'exécution les pointeurs seront plus légers que des objets Point3D à manipuler (constructeur de recopie....etc)

    dans le code (coté développement) ce sera peut-être l'inverse et surement moins élégant.

    des opinions sur l'une ou l'autre de ces méthodes ?

    Damien

    PJ: la classe complète Point3D est en pièce jointe
    Fichiers attachés Fichiers attachés

  2. #2
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 113
    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 113
    Points : 32 958
    Points
    32 958
    Billets dans le blog
    4
    Par défaut
    Une classe Point3D est normalement très petite et la copie ne sera pas trop grave.
    Tu devrais la passer en const& de toute façon.
    Les pointeurs seront plus rapide si tu les transites partout, mais il y a forcément un objet bien réel quelque part sur lequel tu pointes.
    Donc pour chaque pointeur sur T tu as sizeof(T) + sizeof(T*) occupé.
    Imo, aucun intérêt dans ton cas.

    Aussi, on ne fait jamais d'include de cpp dans un cas normal. Les implémentations de template doivent être dans un fichier inlus (les cpp sont normalement compilés par ton IDE déjà), généralement on retrouve les extensions .tpl ou .inl.
    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.

  3. #3
    Nouveau Candidat au Club
    Homme Profil pro
    Développeur Programmation Fonctionelle
    Inscrit en
    Août 2019
    Messages
    9
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Programmation Fonctionelle
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2019
    Messages : 9
    Points : 0
    Points
    0
    Par défaut
    pour l'include du cpp c'est apres un mois de galere sans include que j'en suis arrivé à celà, je sais que ça force le compilo a tout avaler d'un coup et c'est pas top mais quand on utilise des template et la STL c'est la seule solution que j'ai trouvé avec gcc sinon il n'a pas assez d'info à partir du header pour compiler le code.C'est pas faute d'avoir essayer.
    Mais je reconnais c'est pas la façon de coder en C++ que j'avais il y a 20 ans... mais je faisais pas de template et le langage étais different... j'ai vu plein de sujet ailleurs sur ce probléme, mais si tu as une solution qui marche je suis preneur?

    pour en revenir au sujet, je crois quand même que les pointeurs seront plus legers, pour comprendre pourquoi c'est parceque j'utilise une classe Univers avec une liste à parcourir pour savoir si le point3D existe déjà ,et je les veux unique:

    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
    // Universe Class
    // Damien MATTEI
     
    #ifndef UNIVERSE_HPP
    #define UNIVERSE_HPP
     
     
     
     
    template <class T> class Universe {
     
    public:
     
      // data 
      list < Point3D<T> *> point3DptrList;
     
     
      Universe();
     
      ~Universe();
     
      Point3D<T> * createPoint3Dptr(T x,T y,T z); // create a point by checking if it already exist
     
    };
     
    #include "Universe.cpp"
     
    #endif /* UNIVERSE_HPP */
    et mon code Universe.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
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    // Universe Class
    // Damien MATTEI
     
    #include "Point3D.hpp"
    #include "Universe.hpp"
     
    #include "debug.hpp"
     
    // implementations
     
    template <class T> Universe<T>::Universe() {
     
     
    #ifdef DISPLAY_CONSTRUCTOR
      cout << "# Universe constructor # " << this << endl;
    #endif
    }
     
    template <class T> Universe<T>::~Universe() {
     
    #ifdef DISPLAY_CONSTRUCTOR
      cout << "# Universe destructor # "  << this << endl;
    #endif
     
    }
     
     
     
     
    template <class T> ostream&  operator<< (ostream &out, Universe<T> &u)
    {
     
      out << "Universe("  
          << u
          << ")"
        ;
     
      return out;
     
    }
     
     
    // create a point by checking if it already exist in the universe
    template<class T> Point3D<T> * Universe<T>::createPoint3Dptr(T x,T y,T z) {
     
      DEBUG(cerr << "Universe<T>::createPoint3Dptr" << endl;)
      Point3D<T> * pt3d_ptr = new Point3D<T>(x,y,z);
      Point3D<T> & pt3d = *pt3d_ptr;
     
     
      DEBUG(cerr << "Universe<T>::createPoint3Dptr : std::find_if ... " << endl;)
      // i check unicity of the point in Universe i.e to save memory and speed i do not want to have two mathematically identical 3D points
      typename list< Point3D<T> *>::iterator iterP3Dptr = std::find_if(point3DptrList.begin(), point3DptrList.end(),
    								   [&pt3d](Point3D<T> * pt3d_ptr_lambda_param)
    								   { DEBUG(cerr << "Universe<T>::createPoint3Dptr : in Lambda" << endl;)
    								     return  *pt3d_ptr_lambda_param == pt3d; });
     
      DEBUG(cerr << "Universe<T>::createPoint3Dptr : bool found ... " << endl;)
      bool found = (iterP3Dptr != point3DptrList.end());
     
      // we have to add the point to the universe
      if (found) {
          pt3d.~Point3D<T>();
          return *iterP3Dptr; // return the pointer to Point3D
        }
      else {
        DEBUG(cerr << "Universe<T>::createPoint3Dptr : point3DptrList.push_back(pt3d_ptr);" << endl;)
        point3DptrList.push_back(pt3d_ptr);
        return pt3d_ptr;
      }
     
    }
    donc au final par rapport à du code C l'elegance du C++ est de masquer l'utilisation des pointeurs avec des references comme ici:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Point3D<T> * pt3d_ptr = new Point3D<T>(x,y,z);
      Point3D<T> & pt3d = *pt3d_ptr;
    ou dans mon main:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    Universe<float> univ;
     
      cout << " avec des pointeurs et des references vers des point3D " << endl;
     
      Point3D<float> * pt3d_uniq_ptr = univ.createPoint3Dptr(1,0,0);
     
      Point3D<float> & pt3d_uniq_ref = *pt3d_uniq_ptr;
     
      cout << "pt3d_uniq_ref : " << pt3d_uniq_ref << endl;
     
      Point3D<float> * pt3d_uniq_ptr2 = univ.createPoint3Dptr(1,0,0);
     
      Point3D<float> & pt3d_uniq_ref2 = *pt3d_uniq_ptr2;
    mais ça surcharge un peu le code et la mémoire car on voit bien que je dois creer l'objet avec un pointeur:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Point3D<float> * pt3d_uniq_ptr = univ.createPoint3Dptr(1,0,0);
    et ensuite je le manipule avec une réference pour la lisibilité:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Point3D<float> & pt3d_uniq_ref = *pt3d_uniq_ptr;

  4. #4
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 113
    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 113
    Points : 32 958
    Points
    32 958
    Billets dans le blog
    4
    Par défaut
    Plus de code et plus d'erreurs de code..
    On ne fait jamais d'appel explicite au destructeur. Ton truc fait une fuite mémoire si le point existe déjà.
    Non tu n'as aucun intérêt à utiliser de pointeur ici. Non un pointeur n'est pas plus léger. Je t'ai déjà montré qu'un pointeur prend plus de place en mémoire que l'object seul...
    Ta classe Point est ridiculement petite et sera copiée/déplacée sans faire sourciller le moindre CPU.
    Si t'en es à inclure des cpp c'est ta configuration et ton setup qui sont bancales.
    Et pour avoir une collection d'objets unique, il existe std::set.
    Avec une list les pointeurs sont superflus, les éléments ne sont pas désalloués sur les opérations de push_back etc. Avec un vector tu aurais plus de soucis et un pointeur pourrait éventuellement être une solution, et encore, c'est possible que tu sois juste dans l'erreur totale et ne saches pas voir une autre solution meilleure.
    Et même si tu venais à devoir utiliser des pointeurs, ce serait avec des std::unique_ptr.
    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.

  5. #5
    Nouveau Candidat au Club
    Homme Profil pro
    Développeur Programmation Fonctionelle
    Inscrit en
    Août 2019
    Messages
    9
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Programmation Fonctionelle
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2019
    Messages : 9
    Points : 0
    Points
    0
    Par défaut
    visiblement vous lisez pas bien le code , je crée des points unique donc...
    je le detruit moi meme quand la variable intermediaire ne sera pas utilisée,sinon je le garde, apres que je laisse faire ça au compilateur est une possibilité aussi.
    c'est pas clair ce que vous dites sur push_back , je n'ai rien a desaloouer , je stocke justement que le pointeur et non pas l'objet donc pas de recopie inutile, vous comprenez visiblement pas bien le code.
    set est prévu dans une future implementation, je fais des tests comparatifs justement sur la rapidité et l'efficacité, c'est pour ça que je commence par des list et des unordered_set au depart.

  6. #6
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 113
    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 113
    Points : 32 958
    Points
    32 958
    Billets dans le blog
    4
    Par défaut
    Je lis le code, et à l'inverse de toi je semble le comprendre.
    Je vois pt3d.~Point3D<T>();, ceci n'est pas une désallocation de pointeur.
    Pointeur non désalloué, donc fuite mémoire. Peut-être devrais-tu (re)voir ta compréhension du code.
    Si tu veux juste insulter en présentant un code médiocre, je vais pas perdre plus de temps sur ton problème.
    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.

  7. #7
    Nouveau Candidat au Club
    Homme Profil pro
    Développeur Programmation Fonctionelle
    Inscrit en
    Août 2019
    Messages
    9
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Programmation Fonctionelle
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2019
    Messages : 9
    Points : 0
    Points
    0
    Par défaut
    FYI si je commente le destructeur... le point n'est jamais désalloué, voilà pourquoi j'utiilse new et ensuite détruit l'objet, il y aurait une fuite de mémoire au contraire si je ne le faisait pas.

    je peux pas vous montrer l'absence de destruction si elle etais pas faite manuellement puisque justement le destructeur n'est PAS apellé par le code automatiquement mais par contre quand j'apelle le destructeur je le voit bien en mode debug:
    ----------- test Universe ------------------
    # Universe constructor # 0x7ffeeafc3518
    avec des pointeurs et des references vers des point3D
    Universe<T>::createPoint3Dptr
    # Point3D constructor # P3D 0x7fef35400350 (1, 0, 0)
    Universe<T>::createPoint3Dptr : std::find_if ...
    Universe<T>::createPoint3Dptr : bool found ...
    Universe<T>::createPoint3Dptr : point3DptrList.push_back(pt3d_ptr);
    pt3d_uniq_ref : P3D 0x7fef35400350 (1, 0, 0)
    Universe<T>::createPoint3Dptr
    # Point3D constructor # P3D 0x7fef354027d0 (1, 0, 0)
    Universe<T>::createPoint3Dptr : std::find_if ...
    Universe<T>::createPoint3Dptr : in Lambda
    Universe<T>::createPoint3Dptr : bool found ...
    # Point3D destructor # 0x7fef354027d0
    pt3d_uniq_ref2 : P3D 0x7fef35400350 (1, 0, 0)
    --------------------------------------------

    0x7fef354027d0 est bien détruit.

  8. #8
    Membre expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    739
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

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

    Informations forums :
    Inscription : Juin 2011
    Messages : 739
    Points : 3 627
    Points
    3 627
    Par défaut
    Un destructeur ne désalloue pas la mémoire. La mémoire allouée par new/new[] doit être libérée par delete/delete[].

    Il suffit de quelque seconde pour que le code ci-dessous sature la mémoire:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    struct A
    {
      long long a[1024];
    };
     
    int main()
    {
      while (1) {
        A* p = new A;
        p->~A();
      }
    }
    Secondo, on n'utilise pas new et delete, mais des pointeurs intelligents comme std::unique_ptr qui s'occupent d'utiliser delete/delete[] automatiquement.

    Quant à votre problème, les pointeurs et les allocations dynamique ne sont en rien "plus légers":

    - La mémoire est fragmentée.
    - Les déréférencements sont coûteux, d'autant plus que la mémoire est fragmentée.
    - L'overhead d'un new dépasse largement la taille du Point3D.
    - Il fuite de partout.
    - Le code est inutilement complexe et mettre un pointeur dans une référence n'est qu'un cache-misère inutile.

    Concernant std::list, les raisons de l'utiliser sont extrêmement réduites, le choix par défaut devrait être std::vector.

    Votre fonction de hash pour std::unordered_set est bidon: 2 points à la même position n'ont pas le même hash, ils ne seront jamais reconnus comme différent.

  9. #9
    Nouveau Candidat au Club
    Homme Profil pro
    Développeur Programmation Fonctionelle
    Inscrit en
    Août 2019
    Messages
    9
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Programmation Fonctionelle
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2019
    Messages : 9
    Points : 0
    Points
    0
    Par défaut
    on a vraiment l'impression dans ces forum francophones qu'il n'en va que de l'ego et des querelles que declenchent chacqun sans etre constructif , on est vraiment loin du monde anglo saxon,
    je suis aussi fatigué de répondre à des gens qui lisent pas le code.... qui m'ecrit que deux points identiques auront pas le meme hash mais justement ma fonction create_point interdit celà puisqu'elle verifie a l'avance que le point existe ou non
    bon franchement je vais quitter ce forum ou je me suis inscrit aujourd'hui, l'attitude de certains est vraiment trop négative, c'est dommage parceque certaines remarques sont pertinentes par exemple celle sur delete et le destructeur, j'utilisais delete dans une autre version de la classe présentée, je vais retudier celà , je n'avais pas le bon resultat avec delete, aussi des Point3D aloués statiquement ne marchaient pas mieux (plainte du compilo de renvoyer un objet aloué sur la pile...et segfault a l'exec )
    evidemment c'est infiniment plus simple de laisser le C++ gerer tout celà,mais le but n'est pas là ,je cherche à comparer une approche C ou quand on fait un malloc on a la charge desallouer ensuite et une approche C++ , avec aussi une idée derriére la tete comparer celà avec une approche fonctionelle le C++ integrant maintenant des lambdas...

  10. #10
    Nouveau Candidat au Club
    Homme Profil pro
    Développeur Programmation Fonctionelle
    Inscrit en
    Août 2019
    Messages
    9
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Programmation Fonctionelle
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2019
    Messages : 9
    Points : 0
    Points
    0
    Par défaut
    Citation Envoyé par Bousk Voir le message
    Une classe Point3D est normalement très petite et la copie ne sera pas trop grave.
    Tu devrais la passer en const& de toute façon.
    Les pointeurs seront plus rapide si tu les transites partout, mais il y a forcément un objet bien réel quelque part sur lequel tu pointes.
    Donc pour chaque pointeur sur T tu as sizeof(T) + sizeof(T*) occupé.
    ??? un pointeur est une adresse,un nombre , ça prends moins de place qu'une structure ou l'instanciation d'un objet d'une classe, meme si Point3D peut se resumer à 3 nombres,il s'agit d'un exemple, en general un objet de classe peut etre bien plus gros
    Citation Envoyé par Bousk Voir le message
    Imo, aucun intérêt dans ton cas.

    Aussi, on ne fait jamais d'include de cpp dans un cas normal. Les implémentations de template doivent être dans un fichier inlus (les cpp sont normalement compilés par ton IDE déjà), généralement on retrouve les extensions .tpl ou .inl.

  11. #11
    Expert éminent
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 562
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 562
    Points : 7 628
    Points
    7 628
    Par défaut
    Bonjour,

    On n'appelle jamais directement le destructeur. En effet l'appeler détruit bien l'objet (c'est ce que tu vois au debug), mais il faut non seulement détruire mais aussi libérer la mémoire.
    Ce qu'il faut appeler c'est delete unPointeur; ou delete[] unTableau;, ça va détruire l'objet ET libérer la mémoire (vérifie en debug que le destructeur est finalement appelé ou déroule l'exemple de jo_link_noir)

    Ça a été dit plusieurs fois, désolé pour la redite mais comme c'est une base pour débuter...

  12. #12
    Membre expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    739
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

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

    Informations forums :
    Inscription : Juin 2011
    Messages : 739
    Points : 3 627
    Points
    3 627
    Par défaut
    Citation Envoyé par damien_mattei Voir le message
    on a vraiment l'impression dans ces forum francophones qu'il n'en va que de l'ego et des querelles que déclenchent chacun sans être constructif, on est vraiment loin du monde anglo saxon,
    En même temps, vous rejetez en bloc ce qu'on dit en restant borné sur vos positions. Ça agace !

    Citation Envoyé par damien_mattei Voir le message
    je suis aussi fatigué de répondre à des gens qui lisent pas le code.... qui m'ecrit que deux points identiques auront pas le meme hash mais justement ma fonction create_point interdit celà puisqu'elle verifie a l'avance que le point existe ou non
    Cela ne change rien. Si la liste est un std::unordered_set, ce n'est pas à create_point de faire cette vérification: unordered_set le fait déjà. De plus, chaque allocation ayant une adresse unique du moment qu'elle est n'est pas libérée, la moindre vérification de l’existence du pointeur n'a aucun sens. À moins de considérer qu'un pointeur pointant sur une adresse invalide soit juste. Mais se serait totalement con, car ce pointeur ne doit pas être utilisé.

    De plus, pour chercher un point dans cette liste, il faudrait posséder le pointeur... qui serait forcément dans cette liste. Plutôt aberrant. À moins qu'il y a plusieurs fonctions de création, mais j'en doute.

    Et sinon, je parle de la fonction de hash pour std::unordered_set, pas du std::find_if qui lui ne compare pas des adresses.

    je n'avais pas le bon resultat avec delete, aussi des Point3D aloués statiquement ne marchaient pas mieux (plainte du compilo de renvoyer un objet aloué sur la pile...et segfault a l'exec )
    Sûrement parce que vous vous acharnez à vouloir stocker des pointeurs. La règle d'or est de ne jamais les utiliser
    Sauf si nécessaire bien sûr, ce qui n'est pas le cas ici, comme on le répète depuis le début.

    evidemment c'est infiniment plus simple de laisser le C++ gerer tout celà,mais le but n'est pas là ,je cherche à comparer une approche C ou quand on fait un malloc on a la charge desallouer ensuite et une approche C++
    Ce genre de comparaison est une mauvaise idée. Et même en C je ne ferrais pas des pointeurs de Point3D, mais un tableau dynamique de Point3D (donc l'équivalent de std::vector). Au pire, une liste chaînée, mais en aucun cas il n'y aurait un pointeur sur Point3D. Les pointeurs seraient les maillons de la liste et rien d'autre.

    ??? un pointeur est une adresse,un nombre , ça prends moins de place qu'une structure ou l'instanciation d'un objet d'une classe, meme si Point3D peut se resumer à 3 nombres
    Il faut bien que l'instance existe quelque part. Manipuler un pointeur sur Point3D veut dire manipuler un pointeur + un Point3D. Donc il y a bien sizeof(Point3D*) + sizeof(Point3D) qui en mémoire.

    il s'agit d'un exemple, en general un objet de classe peut etre bien plus gros
    Ou bien plus petit. On parle de Point3D depuis le début, les réponses vont dans ce sens. S'il faut manipuler un autre type de structure, les réponses peuvent changer... Ou pas. Surtout si on prend en compte qu'un objet "lourd" peut être géré à l'intérieur d'une classe via une allocation dynamique, donc l'extérieur n'a aucune raison de manipuler des pointeurs.

    De mon point de vue, vous avez des lacunes en ce qui concerne l'allocation dynamique, la durée de vie d'un objet et la manipulation des pointeurs. Si on supprime les pointeurs, les autres problèmes sont fortement diminués, voir disparaît. Et une utilisation systématique de std::make_unique/std::unique_ptr pour l'allocation dynamique soulève généralement des défauts dans la gestion des durées de vie des objets (simplement, car le code ne compile plus ).

    Une approche sans pointeur devrait ressembler à cela:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    std::vector<Point3D<T>> point3DptrList;
     
    Point3D<T> Universe<T>::createPoint3Dptr(T x, T y, T z)
    {
      Point3D<T> p3d{x,y,z};
      auto it = std::find(point3DptrList.begin(), point3DptrList.end(), p3d);
      if (it == point3DptrList.end()) {
        point3DptrList.emplace_back(p3d);
      } 
      return p3d;
    }

  13. #13
    Nouveau Candidat au Club
    Homme Profil pro
    Développeur Programmation Fonctionelle
    Inscrit en
    Août 2019
    Messages
    9
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Programmation Fonctionelle
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2019
    Messages : 9
    Points : 0
    Points
    0
    Par défaut
    Mais la liste n'est pas un unordered_set c'est une liste ou alors on parle pas de la même portion de code, la confusion doit venir de là,
    c'est pas grave on va pas en faire une histoire... c'est un code non pro que je reprends en vacances pour faire des tests, c'est pas un truc pro, je me permets de changer les list en set ou unordered_set à mon gré plus tard pour faire des tests, c'est une sorte de bac à sable

    oui c'est delete qu'il faut utiliser, je l'avais mis il y a 3 jours puis viré car induit en erreur car après avoir fais delete si j'avais gardé un pointeur vers l'objet deleté je pouvais encore l'utiliser... un peu surprenant! Mais effectivement il faut utiliser delete qui appelle le destructeur et ensuite désalloue la mémoire prise par l'objet, je reconnais j'avais pas utilisé ça depuis quelques décennies et récemment je faisais que des allocations statiques sans pointeur, c'est peut-être l'intérêt d'utiliser std::unique_ptr sauf que j'ai fait les modifs en 10 minutes et ça fait mal

    make
    ----------
    COMPILING
    ----------
    g++ -I. -c -Wall -std=c++14 -DDEBUG_BUILD main.cpp
    In file included from main.cpp:3:
    In file included from /Library/Developer/CommandLineTools/usr/include/c++/v1/list:173:
    /Library/Developer/CommandLineTools/usr/include/c++/v1/memory:1805:31: error: call to implicitly-deleted copy
          constructor of 'std::__1::unique_ptr<Point3D<float>, std::__1::default_delete<Point3D<float> > >'
                ::new((void*)__p) _Up(_VSTD::forward<_Args>(__args)...);
                                  ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    /Library/Developer/CommandLineTools/usr/include/c++/v1/memory:1715:18: note: in instantiation of function
          template specialization 'std::__1::allocator<std::__1::__list_node<std::__1::unique_ptr<Point3D<float>,
          std::__1::default_delete<Point3D<float> > >, void *> >::construct<std::__1::unique_ptr<Point3D<float>,
          std::__1::default_delete<Point3D<float> > >, const std::__1::unique_ptr<Point3D<float>,
          std::__1::default_delete<Point3D<float> > > &>' requested here
                {__a.construct(__p, _VSTD::forward<_Args>(__args)...);}
                     ^
    /Library/Developer/CommandLineTools/usr/include/c++/v1/memory:1561:14: note: in instantiation of function
          template specialization
          'std::__1::allocator_traits<std::__1::allocator<std::__1::__list_node<std::__1::unique_ptr<Point3D<float>,
          std::__1::default_delete<Point3D<float> > >, void *> >
          >::__construct<std::__1::unique_ptr<Point3D<float>, std::__1::default_delete<Point3D<float> > >, const
          std::__1::unique_ptr<Point3D<float>, std::__1::default_delete<Point3D<float> > > &>' requested here
                {__construct(__has_construct<allocator_type, _Tp*, _Args...>(),
                 ^
    /Library/Developer/CommandLineTools/usr/include/c++/v1/list:1570:26: note: in instantiation of function
          template specialization
          'std::__1::allocator_traits<std::__1::allocator<std::__1::__list_node<std::__1::unique_ptr<Point3D<float>,
          std::__1::default_delete<Point3D<float> > >, void *> >
          >::construct<std::__1::unique_ptr<Point3D<float>, std::__1::default_delete<Point3D<float> > >, const
          std::__1::unique_ptr<Point3D<float>, std::__1::default_delete<Point3D<float> > > &>' requested here
        __node_alloc_traits::construct(__na, _VSTD::addressof(__hold->__value_), __x);
                             ^
    /Library/Developer/CommandLineTools/usr/include/c++/v1/list:1237:9: note: in instantiation of member function
          'std::__1::list<std::__1::unique_ptr<Point3D<float>, std::__1::default_delete<Point3D<float> > >,
          std::__1::allocator<std::__1::unique_ptr<Point3D<float>, std::__1::default_delete<Point3D<float> > > >
          >::push_back' requested here
            push_back(*__i);
            ^
    ./Universe.hpp:10:26: note: in instantiation of member function
          'std::__1::list<std::__1::unique_ptr<Point3D<float>, std::__1::default_delete<Point3D<float> > >,
          std::__1::allocator<std::__1::unique_ptr<Point3D<float>, std::__1::default_delete<Point3D<float> > > >
          >::list' requested here
    template <class T> class Universe {
                             ^
    /Library/Developer/CommandLineTools/usr/include/c++/v1/memory:2440:3: note: copy constructor is implicitly
          deleted because 'unique_ptr<Point3D<float>, std::__1::default_delete<Point3D<float> > >' has a
          user-declared move constructor
      unique_ptr(unique_ptr&& __u) noexcept
      ^
    1 error generated.
    make: *** [main.o] Error 1
    bon ça aide pas beaucoup sans le code, mais j'ai juste remplacé naïvement point3d<T> * par std::unique_ptr<Point3D<T>>.

    comme dans cet exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    //list < Point3D<T> *> point3DptrList;
      list < std::unique_ptr<Point3D<T>> > point3DptrList;
    c'est dans ces moments là ou je préfère le Scheme ou Lisp au C++

    après utiliser std::unique_ptr<Point3D<T>> pour un point3D et un code qui fait 10 lignes et un point3d que je vais deleter 5 lignes plus bas.... ça revient un peu à traverser la rue escorté de 3 chiens d'aveugle pour pas se faire écraser... mais l'idée est bonne je vais persévérer,

    bon je verrai tout ça demain, il commence à se faire tard....

    oui je crois comprendre ce qui va pas, std::unique_ptr est très contre indiqué ici vu que justement je veux stocker des pointeurs dans des objets abstraits et que je pense que std::unique_ptr va pas aimer du tout ! que je les delegue à d'autres objets que lui même, vu que son rôle est de protéger le pointeur, même en faisant un move ça va être dur...

    je comprends que vous soyez tous contre les pointeurs :-) au départ j'avais fait un code comme vous, c'est lorsque j'ai vu toutes les recopies implicites (j'ai d'autre objets beaucoup plus gros dans le code ailleurs) que j'ai eu l'idée de chercher à minimiser cela, ça parait un peu vieux ce que je vais dire mais quand j'étais étudiant il y a 25 ans le C++ avait la réputation d'être lent par rapport au C et j'ai utilisé il y a une dizaine d'année la bibliothèque boost::array et c'était beaucoup plus lent que des tableaux en C .

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 611
    Points
    30 611
    Par défaut
    Salut,
    Citation Envoyé par damien_mattei Voir le message
    on a vraiment l'impression dans ces forum francophones qu'il n'en va que de l'ego et des querelles que declenchent chacqun sans etre constructif , on est vraiment loin du monde anglo saxon,
    On va essayer d'être clair, car il n'y va en rien de "notre égo".

    Comme il fera preuve d'assez de retenue pour te faire lui-même la remarque, je tiens juste à préciser que je côtoie jo_link_noir depuis des années sur ce forum (et sur d'autres, d'ailleurs) et je peux t'assurer que c'est une personne très compétente.

    Ne vas pas croire que je lui veuille lui passer la pommade, parce que ce n'est pas le cas : c'est un fait.

    Et si tu doute de la véracité de ce qu'il peut te dire -- après tout, tu en as tout à fait le droit -- essaye peut-être "simplement" de répondre à quelques questions:
    Qu'est-ce réellement qu'un pointeur
    1. Quelle est, selon toi, la différence entre une variable qui est créé sur la pile (ex : Point3D<double> maVar; et une variable déclarée sur le tas (ex: Point3D<double> * ptr = new Point3D<double>;
    2. A quoi servent les opérateur new (ou new[]) et delete (ou delete[])
    3. A quoi servent respectivement le constructeur d'une classe et son destructeur
    4. Que fait réellement -- ou du moins selon toi -- la directive #include du préprocesseur
    5. Quelles sont les trois grandes étapes (parce qu'il y en a d'autres, en réalité) par lequel passe notre code pour fournir l'application exécutable

    Je suis sur que jo_link_noir serait parfaitement en mesure de répondre à ces questions.

    A l'inverse, je suis persuadé que tes réponses seront fausses ou "incomplètes".

    C'est normal : a priori, tu es un débutant qui ignore encore bien plus de choses que ce qu'il ne sait, et tout le monde est passé par là. Il faut juste que tu acceptes d'en prendre conscience et que tu en tires les conclusions qui s'imposent : ceux qui savent risquent régulièrement de te recadrer avec tout ce que tu ignores. Ce n'est pas méchant, ce n'est pas pour jouer les caïds. C'est un fait.

    je suis aussi fatigué de répondre à des gens qui lisent pas le code....
    Hé bien, figure toi que je l'ai lu... Et voici ce qu'il m'inspire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // Universe Class
    // Damien MATTEI
     
    #ifndef UNIVERSE_HPP
    #define UNIVERSE_HPP
     
     <snip>
    #include "Universe.cpp"
     
    #endif /* UNIVERSE_HPP */
    On n'inclut JAMAIS un fichier .cpp dans un fichier d'en-tête:

    L'extensions *.cpp (tout comme les extenion *.cc ou *.cxx, ce qui les fait participer au même combat) sont -- clairement -- destinées à servir à l'implémentation. Ce sont les fichiers portant ces extensions qui doivent être traités par le compilateur.

    Si un fichier d'implémentation est inclus dans un fichier d'en-tête, le code que ce fichier contient sera systématiquement inclus dans ... tous les fichiers d'implémentations qui incluent le fichier d'en-tête en question.

    Le compilateur ne se plaindra pas, vu qu'il travaille sur un fichier à la fois. Mais il va placer le code correspondant au contenu du fichier d'implémentation inclut dans ... tous les fichiers objets qu'il va créer.

    Le résultat des courses, c'est que, quand l'éditeur de liens voudras regrouper tous ces fichiers objets en un exécutable unique, il va se retrouver avec ... plusieurs versions du même code binaire exéctuable correpondant au fonctions dont le code est fourni dans le fichier d'implémentation qui est inclus. Et il ne saura pas laquelle il doit choisir dans le cadre de son travail.

    Alors, bien sur, nous sommes dans une situation toute particulière, du fait que la classe Universe est une classe template; ce qui implique -- entre autres -- que le code binaire exécutable issus de cette classe ne sera généré par le compilateur qu'à partir du moment où ... on dispose des paramètres templates permettant de spécialiser complètement la classe. Autrement dit, quand on verra effectivement apparaitre un code proche de Point3D<float> truc;.

    Et nous pourrions -- peut-être -- considérer comme "correct" le fait que le code des fonctions de cette classe template soit dans un fichier dont l'extension *.cpp. En tout état de cause, le fait que le préprocesseur accepte la directive #include "Universe.cpp" semble vachement plaider en ta faveur, non

    Seulement, voilà : tu n'écris pas ton code que pour toi. Et c'est encore plus vrai à partir du moment où tu le présente sur un forum, vu que, si tu veux que les gens puissent te répondre, ils devront comprendre ton code, et qu'ils vont donc le lire et l'analyser.

    Arrivé à ce stade, tu vas te prendre un tout premier mur de plein fouet sur le nez : le mur des conventions. Car, pour que deux personnes arrivent à se comprendre, il faut des conventions : c'est parce que l'on utilise la même convention pour désigner une cuiller que tu ne risques pas de me filer un couteau quand je t'en demande une!

    Même si on n'a pas eu notre mot à dire lorsque les conventions ont été établies, même si on les trouve absurdes (j'aurais préféré pouvoir te demander le machin et être sur de pouvoir boire ma soupe), on se heurte de pleins fouets aux millions de développeurs C++ qui se sont mis d'accord sur un fait désormais immuable: les fichiers dont l'extension est *.cpp / *.cc / *.cxx sont les fichiers qui permettent au compilateur de générer les fichiers objets. Tu peux donc choisir n'importe quelle extension pour désigner un fichier qui devra être inclus dans un autre, sauf, bien sur des extensions *.cpp, *.cc ou *.cxx.

    Et, pour bien faire, toujours à cause des conventions, tu serait sympa d'éviter les extensions qui sont "traditionnellement" associées à des formats spécifiques, telles que *.htm, *.php *.jpg ou *.png
    qui m'ecrit que deux points identiques auront pas le meme hash mais justement ma fonction create_point interdit celà puisqu'elle verifie a l'avance que le point existe ou non
    Alors, déjà, dans le code que tu nous montre, il manque la fonction hash pour que l'on puisse en avoir la certitude.

    De plus, si deux points identiques sont destinés à être identifiés (au travers du hash obtenu) de manière différente, peux tu me dire pourquoi tu as "perdu ton temps" définir l'opérateur ==

    Car, de deux choses l'une: ou bien tu veux que deux points identiques soient clairement considérés comme différents (raison de ta fonction de hash) ou bien, tu veux pouvoir les considérer comme identiques (raison de ton opérateur == ).

    Mais, avoue qu'une logique du genre de
    j'ai deux points qui sont considérés comme différents, mais je voudrais malgré tout savoir s'il ne correspondent pas à la même coordonnée
    ca a sérieusement de quoi retourner les cerveaux les mieux accrochés, non

    J'dis ca, j'dis rien... Mais je n'en pense pas moins
    bon franchement je vais quitter ce forum ou je me suis inscrit aujourd'hui, l'attitude de certains est vraiment trop négative
    De toutes manières, tu feras exactement ce que tu voudras

    Mais, avant de jeter la pierre "sur le forum" ou même sur une tête bien particulière, tu ferais peut-être bien de faire ta propre auto critique

    Car, de mon point de vue tout à fait extérieur, toutes les remarques faites par jo_link_noir lors de sa première intervention sont parfaitement justifiées. Et quand on lit ta première réponse, à savoir que
    pour l'include du cpp c'est apres un mois de galere sans include que j'en suis arrivé à celà, je sais que ça force le compilo a tout avaler d'un coup et c'est pas top mais quand on utilise des template et la STL c'est la seule solution que j'ai trouvé avec gcc sinon il n'a pas assez d'info à partir du header pour compiler le code.C'est pas faute d'avoir essayer.
    Ben, non, justement, il y a des solutions : on utilise une extension *.ipp ou *.inc, et ca marche aussi, sans aller foutre un coup de pied dans la fourmilière en utilisant une extension qui n'est pas clairement destinée à ce qu'on veut faire.
    pour en revenir au sujet, je crois quand même que les pointeurs seront plus legers, pour comprendre pourquoi c'est parceque j'utilise une classe Univers avec une liste à parcourir pour savoir si le point3D existe déjà ,et je les veux unique:
    Hé bien, figure toi que, comme dit mon vénéré père:
    Citation Envoyé par papa à koala
    Il faut laisser croire les béguines, elles sont spécialistes et payées pour!
    Et que, sur ce point, tu ferais largement mieux de respecter ce genre de conseil:

    Car un pointeur n'est jamais qu'une valeur numérique entière (généralement non signée) qui représente l'adresse à laquelle on (devrait) espère trouver une donnée du type indiqué. Ce n'est donc pas plus léger, bien au contraire, que l'on se balade avec une donnée (codée sur 32 ou 64 bits, selon l'architecture) en plus de la donnée qui nous intéresse.

    Le seul avantage qu'il pourrait y avoir à utiliser des pointeurs, c'est que ca évite les copie (de la donnée pointée s'entend) lorsqu'on les utilise pour déclarer un paramètre de fonction. Mais, d'un autre coté, c'est la donnée qui représente le pointeur (donc, d'office 32 ou 64 bits) qui devra être copiée

    Et puis, les pointeurs, c'est une source d'emmerdement sans nom, parce que:
    1. ca peut toujours représenter une adresse mémoire invalide (nullptr depuis C++11, avant on utilisait volontier NULL)
    2. la mémoire allouée au pointeur peut avoir été libérée, sans que l'adresse mémoire représentée par le pointeur n'ait été mise à jour (pour représenter nullptr); ce qui fera planter ton application
    3. on est toujours occupé à se poser la question de savoir s'il faut ou non libérer la mémoire allouée au pointeur
    4. on risque de "laisser passer la dernière occasion" de libérer correctement la mémoire; ce qui occasionne des fuites mémoires
    5. si on tient, en plus, compte du système d'exceptions, ca devient le bordel

    Et tu nous présente un peu plus de code, qui contient beaucoup plus d'erreurs. Par exemple, le fameux
    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
    // create a point by checking if it already exist in the universe
    template<class T> Point3D<T> * Universe<T>::createPoint3Dptr(T x,T y,T z) {
     
      DEBUG(cerr << "Universe<T>::createPoint3Dptr" << endl;)
      Point3D<T> * pt3d_ptr = new Point3D<T>(x,y,z);
      Point3D<T> & pt3d = *pt3d_ptr;
     
     
      DEBUG(cerr << "Universe<T>::createPoint3Dptr : std::find_if ... " << endl;)
      // i check unicity of the point in Universe i.e to save memory and speed i do not want to have two mathematically identical 3D points
      typename list< Point3D<T> *>::iterator iterP3Dptr = std::find_if(point3DptrList.begin(), point3DptrList.end(),
    								   [&pt3d](Point3D<T> * pt3d_ptr_lambda_param)
    								   { DEBUG(cerr << "Universe<T>::createPoint3Dptr : in Lambda" << endl;)
    								     return  *pt3d_ptr_lambda_param == pt3d; });
     
      DEBUG(cerr << "Universe<T>::createPoint3Dptr : bool found ... " << endl;)
      bool found = (iterP3Dptr != point3DptrList.end());
     
      // we have to add the point to the universe
      if (found) {
          pt3d.~Point3D<T>();
          return *iterP3Dptr; // return the pointer to Point3D
        }
      else {
        DEBUG(cerr << "Universe<T>::createPoint3Dptr : point3DptrList.push_back(pt3d_ptr);" << endl;)
        point3DptrList.push_back(pt3d_ptr);
        return pt3d_ptr;
      }
     
    }
    Alors, là... C'est le pompom, car on pourrait presque faire une remarque à chaque ligne (et si pas, on n'en est pas loin ) !
    Déjà, pourquoi voudrais tu renvoyer un pointeur de Point3D Etant donné qu'il sera de toutes manières inclus dans une collection de Universe, pourquoi ne renverrais tu pas une référence (de préférence constante) sur ce point Pourquoi la fonction ne prendrait pas la forme de template<class T> Point3D<T> /*const*/ & Universe<T>::createPoint3Dptr(T x,T y,T z) Bon, bien sur, cela nécessitera quelques adaptations "ailleurs", mais, au moins, on a la certitude que le point renvoyé existe...

    Tant qu'à faire, pourquoi "se faire chier" à créer dynamiquement un nouveau point, alors que c'est pour ... prendre une référence sur le point pointé

    Les lignes 47 et 48 (dans ton code) pourraient être remplacée par une ligne absolument unique:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Point3D<T>  pt3d = Point3D<T>(x,y,z);
    Ce qui nous donnerait une très bonne raison de changer la forme prise par point3DptrList (qui pourrait, au passage, être renommée point3DList, mais ce n'est qu'un détail) pour qu'elle prenne la forme de std::list<Point3D<T>> point3DList;:
    on passerait d'une liste de pointeur sur des Point3D à une liste de ... Point3D et on gagnerait une indirection tout à fait inutile

    Mais cela nous permettrait de simplifier la suite, à savoir que la ligne
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     typename list< Point3D<T> *>::iterator iterP3Dptr = std::find_if(point3DptrList.begin(), point3DptrList.end(),
    								   [&pt3d](Point3D<T> * pt3d_ptr_lambda_param)
    								   { DEBUG(cerr << "Universe<T>::createPoint3Dptr : in Lambda" << endl;)
    								     return  *pt3d_ptr_lambda_param == pt3d; });
    pourrait être simplifiée pour prendre la forme de(si tu es vraiment allergique au C++ moderne)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     typename list< Point3D<T> >::iterator iterP3D = std::find_if(point3DptrList.begin(), point3DptrList.end(),
    								   [&pt3d](Point3D<T> pt3d_lambda_param)
    								   { DEBUG(cerr << "Universe<T>::createPoint3Dptr : in Lambda" << endl;)
    								     return  pt3d_ptr_lambda_param == pt3d; });
    Note que tu n'as quand même pas l'air d'y être si allergique que cela, vu que tu utilises les expressions lambda... Allez, un petit effort pour donner un coup de moderne à ce code, et on y est :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    auto iterP3D = std::find_if(point3DptrList.begin(), point3DptrList.end(),
                                [&pt3d](Point3D<T> pt3d_lambda_param)
                                { DEBUG(cerr << "Universe<T>::createPoint3Dptr : in Lambda" << endl;)
                                  return  pt3d_ptr_lambda_param == pt3d; });
    On en arrive à la fameuse ligne qui ne fait surement pas ce que tu espère

    Car sais tu ce qu'elle fait réellement

    A voir ton code, on croirait bien volontiers que tu t'attende à ce que cette ligne libère correctement la mémoire du pointeur, non

    Mais, as tu remarqué que tu travaille sur ... la référence du point pointé par pt3d_ptr Il n'y a donc absolument aucune chance que cela influe en quoi que ce soit sur la mémoire qui a été allouée à pt3d_ptr!!!

    Ce que cela va avoir comme résultat -- car cela va effectivement avoir une incidence -- c'est de placer l'objet (de type Point3D) pointé par pt3d_ptr dans un état que l'on peut au mieux qualifier de "indéterminé":

    La variable est sensé ne plus exister, vu que le destructeur est passé dessus. Si tu essaye d'accéder à son contenu (sans doute ses données x, y et z), tu risques donc d'avoir des valeurs tout à fait aberrantes. Mais, d'un autre coté, l'espace mémoire suffisant à la représentation de cette variable a été créé sur le tas au travers de l'allocation dynamique de la mémoire, vu qu'on a utilisé l'instruction Point3D<T> * pt3d_ptr = new Point3D<T>(x,y,z);.

    Si tu ne quittais pas la fonction tout de suite après cette ligne, tu pourrais donc encore parfaitement décider de "remettre" l'élément pointé par pt3d_ptr dans un état cohérent afin de le "réutiliser".

    De plus, le seul moyen de libérer correctement la mémoire pour le pointeur serait de passer par l'appel à delete. Or, tu ne fais aucun appel à delete pour pt3d_ptr. Si bien que, comme tu n'auras plus nulle part l'occasion de récupérer ton pointeur sur pt3d_ptr, tu te retrouveras avec ... une belle fuite mémoire.

    Alors, bien sur, il reste la possibilité du else pour nous "sauver", dans le sens où si tu n'as effectivement pas trouvé le point équivalent dans la liste (des points qui existent déjà), ton pointeur sera ajouté à la liste, et nous aurons "une nouvelle chance" de récupérer ce pointeur "par la suite".

    Mais dis toi bien que: plus point3DptrList contiendra d'éléments, plus le risque encouru de tomber sur un point qui est déjà dans la liste sera grand (ca parrait logique, non ?), et plus tu occasionnera de fuites mémoires. Faisons tourner ton application suffisamment longtemps, et tu finira par te rendre compte que tu ne peux plus rien faire avec ton ordinateur.

    C'est aussi simple que cela !
    evidemment c'est infiniment plus simple de laisser le C++ gerer tout celà,mais le but n'est pas là ,je cherche à comparer une approche C ou quand on fait un malloc on a la charge desallouer ensuite et une approche C++
    Pour quoi faire Te viendrait-il à l'idée de comparer l'approche du latin (tu sais: rosa, rosa, rosam, rosae, rosae, rosa,rosae, rosa, rosas, rosarum, rosis, rosis) avec le français (une rose, des roses)

    Le principe est exactement le même : le francais descend en droite ligne du latin, tout comme le C++ descend en droite ligne du C. Et pourtant, le français est une langue totalement différente du latin; tout comme le C++ est un langage totalement différent du C.
    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. #15
    Nouveau Candidat au Club
    Homme Profil pro
    Développeur Programmation Fonctionelle
    Inscrit en
    Août 2019
    Messages
    9
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Programmation Fonctionelle
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2019
    Messages : 9
    Points : 0
    Points
    0
    Par défaut
    tu as tout a fait raison Koala premier, les pointeurs ne servent à rien en informatique!
    et ce forum non plus ne sert à rien alors.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 611
    Points
    30 611
    Par défaut
    Citation Envoyé par damien_mattei Voir le message
    tu as tout a fait raison Koala premier, les pointeurs ne servent à rien en informatique!
    Je n'ai jamais dit que les pointeurs ne servent à rien!

    J'ai, surtout dit que les pointeurs -- et surtout les pointeurs bruts -- c'est une crasse qu'il vaut mieux éviter à chaque fois que faire se peut. Car, ce n'est pas comme si on manquait d'alternatives aux pointeurs bruts: entre
    • les pointeurs intelligents qui permettent de s'assurer que la mémoire sera libérée lorsque l'on perd la dernière référence à l'adresse mémoire
    • les références qui ont au moins le bon gout de ne pas nous obliger de manière systématique à vérifier si la donnée existe bel et bien
    • et les différentes collections qui ont le bon gout de s'occuper de la gestion de la mémoire pour nous

    tu avoueras que, avant de décider de se casser la tête -- ne serait-ce que parce que C++ est un langage à exceptions -- à gérer la mémoire à la main, on déjà pas mal de pistes à parcourir !

    Finalement, le seul cas où l'on n'aura sans doute pas vraiment le choix ce sera le cas d'une hiérarchie de classes polymorphes pour lesquelles nous souhaitons pouvoir regrouper l'ensemble des éléments issus de cette hiérarchies dans une collection d'objets connus comme étant du type de base.

    Et encore, vu qu'on préféreras toujours utiliser les pointeurs intelligents pour ce qui est de la gestion de la mémoire.
    et ce forum non plus ne sert à rien alors.
    Et, après ca, c'est nous qui sommes désagréable

    En attendant, je remarque que tu n'as pas répondu à mes cinq première questions

    Allez, pour te montrer que ce n'est pourtant pas si difficile je vais répondre à la tienne:

    Si tu lisais un tant soit peu les message d'erreurs émis par ton compilateur, tu aurais trouvé directement la raison pour laquelle cela ne veut pas compiler... Parce que figures-toi qu'elle est écrite en TOUTES LETTRES. Mais, comme il est vrai qu'elle est peut-être noyée dans un tas d'informations qui n'ont rien à voir, je te la copie ici:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    g++ -I. -c -Wall -std=c++14 -DDEBUG_BUILD main.cpp
    In file included from main.cpp:3:
    In file included from /Library/Developer/CommandLineTools/usr/include/c++/v1/list:173:
    /Library/Developer/CommandLineTools/usr/include/c++/v1/memory:1805:31: error: call to implicitly-deleted copy
          constructor of 'std::__1::unique_ptr<Point3D<float>, std::__1::default_delete<Point3D<float> > >'
                ::new((void*)__p) _Up(_VSTD::forward<_Args>(__args)...);
                                  ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Et je t'en donne même la traduction:
    tu fais appel au constructeur de copie de std::unique_ptr alors que cette fonction a explicitement été "effacée"
    Et, quelque part, cela a quelque chose de logique, vu que, comme le nom l'indique si bien, std::unique_ptr est sensé être ... le seul responsable de la durée de vie du pointeur sous-jacent.

    Il semble aller de soi que, si on en venait à permettre la copie d'un unique_ptr, on se retrouverait avec deux unique_ptr dont le pointeur sous-jacent pointe... vers la même adresse mémoire, et que si l'un des deux venait à libérer la mémoire, l'autre serait dans le caca...

    Or, c'est vraiment pas de bol, les collections de la STL ajoutent (par exemple, au travers de la fonction push_back, pour les listes et les tableaux) les élément par ... copie.

    C'est justement en pensant à ce genre de problème que le commité a mis au point ce que l'on appelle la "sémantique de déplacement". Pour faire simple, au lieu de faire une copie "membre à membre" des données qui composent une variable, on peut désormais aussi envisager de la "vampiriser", dans le sens où l'on va littéralement "arracher" les données de la variable d'origine pour pouvoir les donner à la variable de destination.

    Bien sur, cela veut dire que la variable d'origine se trouve dans un état pour le moins inconsistant. Mais, en l'état, on s'en fout pas mal, car le but est -- littéralement -- de ne plus utiliser que ... la variable de destination (et, dans le cadre d'un unique_ptr de s'assurer qu'il n'y en aura jamais qu'un seul dont le pointeur sous-jacent pointe vers une adresse bien particulière)

    De plus, la technique n'a véritablement de l'intérêt que ... quand l'une des données qui composent la variable est un pointeur (pour lequel on a eu recours à l'allocation dynamique de la mémoire).

    Cependant, le comportement par défaut reste la copie. Si on veut avoir recours à la sémantique de déplacement, il faut l'indiquer de manière explicite en utilisant std::move. Mais cela nous permet de remplir nos différentes collections avec des std::unique_ptr. Au lieu d'avoir un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    std::list<std::unique_ptr<Point3D<double>>> maListe;
    std::unique_ptr<Point3D<double>> ptr = std::make_unique<Point3D<Double>>(); // ou n'importe quel autre moyen d'obtenir un unique_ptr de ton choix
    maListe.push_back(ptr); //BOUM : la copie n'est pas autorisé
    nous aurons un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    std::list<std::unique_ptr<Point3D<double>>> maListe;
    std::unique_ptr<Point3D<double>> ptr = std::make_unique<Point3D<Double>>(); // ou n'importe quel autre moyen d'obtenir un unique_ptr de ton choix
    maListe.push_back(std::move(ptr)); //OK: ptr est "vampirisé" au profit de l'élément ajouté à la liste
    /* !!!!   le pointeur sous-jacent de ptr pointe désormais sur nullptr !!!
       !!!!   toute tentative de le déréférencer  (ex : ptr->x ou        !!!!
       !!!!   ptr.get()->x) provoquera un crash de l'application         !!!!
    Tu vois, comme il est facile de répondre à une question, parfois même en donnant bien plus d'informations que ce à quoi tu te serais attendu à la base (car j'ai abordé pas mal de points connexes...)

    Alors, si tu faisais un effort, et que tu répondais à mes cinq questions (tu n'est pas obligé d'être aussi complet que moi), tu sais, juste pour nous montrer que tu n'es pas le genre de type qui ne supporte aucune remarque et qui s'attend forcément à ce que tout le monde tombe en pâmoison devant l'énorme qualité (hum hum ...) de son code .
    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. Pointeur vers objet dans un vecteur
    Par julieng31 dans le forum C++
    Réponses: 3
    Dernier message: 27/09/2013, 10h29
  2. Réponses: 5
    Dernier message: 21/06/2013, 22h13
  3. Passage de tableau de pointeurs vers objets
    Par gnulix dans le forum C++
    Réponses: 5
    Dernier message: 14/04/2007, 20h41
  4. Réponses: 4
    Dernier message: 04/02/2007, 01h06

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