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 :

Au sujet des avantages de la STL


Sujet :

Langage C++

  1. #1
    Invité
    Invité(e)
    Par défaut Au sujet des avantages de la STL
    Cette discussion dérive de structure dedonnée et tableau et démarée suite à l'intervention de davidbrcz
    Citation Envoyé par Davidbrcz Voir le message
    Plus de problème de gestion de la mémoire, enveloppe RAII sur tes données (comprendre que la destruction se fait automatiquement)
    Exemple:
    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
     
    #include <iostream>
    #include <string>
    #include <vector>
    #include <limits>
     
    using namespace std;
     
    struct foo{
      string x;
      string y;
    };
     
    int main ()
    {
      short i, a;
     
      cout << "Longueur du tableaux : ";
      cin >> a;
      std::vector<foo> pointer(a); //ici on  réserve un tableau pour a objet, mais on pourrait en ajouter au fur et à mesure.
     
      cin.ignore(numeric_limits<streamsize>::max(), '\n');
     
      for(i = 0 ; i < a ; i++){
        cout << "Entrez une phrase pour tableau 1 array " << i << " : ";
        getline(cin, pointer[i].x);
      }
     
      for(i = 0 ; i < a ; i++){
        cout << "Entrez une phrase pour tableau 2 array " << i << " : ";
        getline(cin, pointer[i].y);
      }
     
      for(i = 0 ; i < a ; i++){
        cout << pointer[i].x << endl;
      }
     
      for(i = 0 ; i < a ; i++){
        cout << pointer[i].y << endl;
      }
      return 0;
    }
    Les vectors, c'est bon, il faut en manger (et la STl en général) car ca offrent plein de possibilité. Ajout d'élement simplifié, accès à la taille de la collection,...

    Recherche sur google et une fois que tu y as gouté, tu ne t'en passes plus.
    =====================================================================================================
    =====================================================================================================
    Citation Envoyé par Davidbrcz Voir le message
    Ajout d'élement simplifié,
    C'est peut être pas la caractéristique que je mettrais en premier, ça...

    Les vector<> c'est bien tant que tu ajoutes des éléments à la fin, mais pour tous les autres cas, c'est pas adapté (il faut des queue ou des lists).

    Pareil pour les suppressions d'ailleurs (la mémoire n'est jamais récupérée, dans un vector, à moins de recourir à des trucs).

    Donc... les vecteurs c'est bien pour allouer des tableaux de taille fixe, déterminée à l'exécution, mais pour les tableaux dont la taille varie, gaffe!

    Francois

  2. #2
    Invité
    Invité(e)
    Par défaut
    Pour moi le fait de connaitre la taille... J'avoue ne pas voir le problème avec les delete[] (le fait d'avoir fait du C avant sans doute...) : quand on ouvre une accolade on la ferme, derrière chaque new, y'a un delete... si on n'y arrive pas, ben, on se met au VB...

    En revanche, en C, on passe son temps à garder la taille de ses tableaux... size(), c'est un sacré bon truc, quand meme...

    Francois

  3. #3
    Rédacteur

    Avatar de Davidbrcz
    Homme Profil pro
    Ing Supaéro - Doctorant ONERA
    Inscrit en
    Juin 2006
    Messages
    2 307
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : Suisse

    Informations professionnelles :
    Activité : Ing Supaéro - Doctorant ONERA

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 307
    Points : 4 732
    Points
    4 732
    Par défaut
    Citation Envoyé par fcharton Voir le message
    Pour moi le fait de connaitre la taille... J'avoue ne pas voir le problème avec les delete[] (le fait d'avoir fait du C avant sans doute...) : quand on ouvre une accolade on la ferme, derrière chaque new, y'a un delete... si on n'y arrive pas, ben, on se met au VB...

    En revanche, en C, on passe son temps à garder la taille de ses tableaux... size(), c'est un sacré bon truc, quand meme...

    Francois
    Bah en cas d'exception, disposer d'une enveloppe RAII permet d'éviter les problèmes de gestion de mémoire et d'éviter toutes les fuites.
    "Never use brute force in fighting an exponential." (Andrei Alexandrescu)

    Mes articles dont Conseils divers sur le C++
    Une très bonne doc sur le C++ (en) Why linux is better (fr)

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    En réalité, il y a deux problèmes qu'il s'agit de bien distinguer...

    Le premier sur lequel il faut mettre l'accent est le choix du conteneur proprement dit, mais c'est un problème qui se rencontre aussi en C (avec en plus le fait de *souvent* être tenté de tout recoder à partir de zéro) lorsqu'il s'agit de choisir la "moins mauvaise" structure dynamique pour l'usage que l'on en fait...

    Que ce soit en C ou en C++, l'ajout d'élément ailleurs qu'en début ou en fin de tableau est toujours particulièrement chronophage du fait de la nécessité de réallouer un espace mémoire (même si la fréquence d'apparitions de la réallocation peut être amortie en respectant quelques principes simples, ce qui est le cas de std::vector et qui doit être codé explicitement dans ce but en C) avant de décaler une partie (à déterminer) des éléments d'un nombre (lui aussi à déterminer) de case(s) suffisant pour, enfin, être en mesure d'insérer le(s) élément(s) que l'on veut insérer

    Le gros avantage des tableaux (dynamiques ou non) en C comme en C++ tient surtout au fait de l'accès aux différents élément en temps constant: pour tout tableau contenant N élément et pour autant que 0<=M<N, l'accès à l'indice M se fait en un temps particulièrement intéressant

    Mais il faut bien se rendre compte que, selon l'utilisation que l'on fait des éléments contenus, une pile, une file une liste (simplement ou doublement chainée) ou même un arbre binaire peut s'avérer autrement plus intéressant à manipuler:

    S'il n'est pas question de trier les éléments et que nous sommes dans une logique FIFO ou LIFO, la pile et la file sont particulièrements intéressantes

    Il ne faut, finalement, pas plus de temps pour intervertir deux éléments dans une liste simplement chainée que pour le faire dans un tableau (il "suffit" dans les deux cas de trois opération xor ), modulo le fait qu'il soit peu raisonnable d'envisager une recherche dichotomique des éléments à intervertir.

    Par contre, l'insertion dans une liste se fera en temps constant quelle que soit la position à laquelle elle doit survenir

    Et nous pourrions passer la nuit (enfin, non, pas tout à fait étant donné le peu de structures dynamiques qui existent) à deviser des avantages comparés de chaque structure

    Le deuxième problème tient à que l'on appelle couramment RAII (Ressource Acquisition Is Initialisation) et auquel il faudrait toujours penser à associer son inverse que nous pourrions appeler RRID( Ressource Release Is Destruction) si le terme existait, et, finalement, le respect de la forme canonique orthodoxe de Coplien, du moins quand elle s'applique

    En effet, l'ensemble des classes de la S(T)L assurent une initialisation et une destruction correcte des membres qui les composent, et c'est aussi vrai (bien évidemment) pour les conteneurs

    Mais il faut aussi prendre en compte que, pour les classes copiables et assignables (car certaines classe sont déclarées pour être ni copiables ni assignables comme les flux, par exemple), nous avons l'assurance que la copie et l'assignation ne mèneront pas à des catastrophes dues soit à des fuites mémoire soit à des tentatives de double libération de la mémoire.

    Et il faut avouer que, si le but est d'arriver à une structure dynamique créée "de toute pièces" respectant la forme canonique orthodoxe de coplien, il devient réellement très facile, par distraction, par manque d'habitude ou simplement par manque de sommeil, d'en arriver effectivement à des situations catastrophiques.

    Le but de la S(T)L est de fournir quelque chose de robuste qui nous permet de nous focaliser sur les choses qui sont réellement importantes en rendant transparent pour l'utilisateur tout ce qui a trait à la gestion dynamique de la mémoire.

    Cela ne veut bien sur pas dire qu'un programmeur C++ ne doit absolument pas connaitre le fonctionnement particulier de ce genre de gestion de la mémoire, mais met bel et bien l'accent sur le fait qu'il doit veiller à ne l'utiliser que lorsque c'est réellement nécessaire et indispensable.

    Du moins, pour la règle générale.

    Ensuite, nous sommes effectivement conscients que cette règle générale souffre, comme toute règle, d'exceptions sur des cas bien particuliers.

    Et nous sommes donc bien tous d'accord sur le fait que, de manière exceptionnelle, il peut être intéressant, dans certaines situations, d'abandonner l'usage de la S(T)L au profit de techniques directement issues du C.

    Mais ce genre de décision ne devrait en tout état de cause être prise qu'après avoir clairement optimisé au maximum les algorithmes et s'être assuré que la complexité que cet abandon va impliquer aura bel et bien un effet positif sur les performances.

    J'ai bien conscience que ce que je vais écrire peut avoir de quoi choquer du monde, mais, selon moi, la première qualité d'un code, avant même de faire ce qu'on attend de lui, c'est d'être facilement lisible et compréhensible par la personne qui l'a devant les yeux...

    Or, il n'y a rien à faire, un code qui présente trois instructions distinctes (et "auto commentées") sera en tout état de cause toujours plus simple à comprendre qu'un code obtenant le même résultat en dix instructions distinctes (et tout aussi auto commentées).

    Pour toutes ces raisons et parce que je suis de ceux qui estiment qu'il n'y a pas de mauvaise manière de procéder, mais qu'il y a des manières plus (ou moins) efficace de le faire, je t'avouerai que je trouverai souvent plus efficace de recourir à la STL que de "réinventer la roue"
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  5. #5
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Le gros avantage des tableaux (dynamiques ou non) en C comme en C++ tient surtout au fait de l'accès aux différents élément en temps constant: pour tout tableau contenant N élément et pour autant que 0<=M<N, l'accès à l'indice M se fait en un temps particulièrement intéressant
    Note bien que c'est aussi vrai d'une liste, quand on la parcourt dans l'ordre (ou l'ordre inverse si elles sont doublement chaînées). Et ce parcours ordonné est un cas extrèmement courant. En revanche, un tableau aura une représentation plus simple et plus compacte qu'une liste, et une liste sera lente en accès aléatoire.

    C'est un point qui me semble assez curieux, soit dit en passant. La STL fournit des vector<> comme des list<>, mais il me semble que les secondes sont nettement moins utilisées.

  6. #6
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Citation Envoyé par fcharton Voir le message
    Pour moi le fait de connaitre la taille... J'avoue ne pas voir le problème avec les delete[] (le fait d'avoir fait du C avant sans doute...) : quand on ouvre une accolade on la ferme, derrière chaque new, y'a un delete... si on n'y arrive pas, ben, on se met au VB...

    On pourrait objecter l'Argument de Fénéantise.
    Bizarrement, je préfère écrire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    class Panier
    {
    public:
       Panier(){}
       Panier(int numFruit):panier_(numFruit){}
    private:
       //..
       std::vector<Fruit> panier_;
    };
    à
    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
     
    class Panier
    {
    public:
       Panier():panier_(NULL), numFruit_(0){}
     
       explicit Panier(std::size_t numFruit):
       panier_(new Fruit[numFruit]), 
       numFruit_(numFruit)
       {}
     
       Panier(const Panier& other):
       numFruit_(other.numFruit_), 
       panier(new Fruit[other.numFruit_])
       {
          for(std::size_t i = 0; i < numFruit; ++i)  
             panier_[i] = other.panier_[i];
       }
     
       Panier& operator=(const Panier& other) 
       { 
          if (numFruit_ == other.numFruit_)  
             for (std::size_t i = 0;  i < numFruit_; ++i) 
               panier_[i] = other.panier_[i]; 
          else
          { 
             Panier tmp(other); 
             this->swap(tmp);
          }
          return *this;
       } 
     
       ~Panier() { delete [] panier_; }
     
       void swap( Panier& other) 
       {
          std::swap(numFruit_, other.numFruit_);  
          std::swap(panier_, other.panier_); 
       }
     
    private:
       //...
       Fruit* panier_;
       std::size_t numFruit;
     };

  7. #7
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Pour continuer l'argument de arzar je dirais que justement si tu veux faire des delete etc, en gros géré la mémoire alors autant faire du C. En C++ on a tous ce qui faut pour encapsuler ces comportements et éviter de courir à la catastrophe (ou pas d'ailleurs, après tout avec de la rigueur ça passe.) alors pourquoi s'en priver?
    On peut rajouter l'exemple de la désallocation dans le cas d'une exception qui même simple t'obliges à gérer deux fois la libération de la donnée.. (que ça soit un lock, de la mémoire, un fichier etc).
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par Goten Voir le message
    Pour continuer l'argument de arzar je dirais que justement si tu veux faire des delete etc, en gros géré la mémoire alors autant faire du C. En C++ on a tous ce qui faut pour encapsuler ces comportements et éviter de courir à la catastrophe (ou pas d'ailleurs, après tout avec de la rigueur ça passe.) alors pourquoi s'en priver?
    On peut rajouter l'exemple de la désallocation dans le cas d'une exception qui même simple t'obliges à gérer deux fois la libération de la donnée.. (que ça soit un lock, de la mémoire, un fichier etc).
    Je suis, de manière générale, d'accord avec toi, mais il me semble nécessaire d'apporter une certaine nuance...

    Effectivement, dans 90 à 95 % des cas, il serait dommage de se priver des facilités apportées par le langage.

    Mais dans les 5 à 10 % des cas restant, il peut effectivement arriver que la S(T)L ne soit pas satisfaisante et que, pour des raisons propres au projet, ou à une partie du projet, on puisse décider de s'en passer, tout en ne voulant pas forcément se tourner vers un langage "de plus bas niveau"...

    En effet, C++ tient cette position particulière qui consiste à être le langage "haut niveau" permettant le plus une gestion "bas niveau" (à moins que ce soit la position d'être le langage "bas niveau" permettant le plus une gestion "haut niveau" )

    Je trouverais (à titre personnel) aberrant de décider de se passer de l'ensemble parce que, sur un code de 1000 instructions, nous nous trouvons confronté à 100 ou 200 instructions qui justifient des optimisations impossibles à apporter avec la S(T)L

    (évidemment, les valeurs citées le sont à titre indicatif et peuvent être multipliées à l'envie )
    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

  9. #9
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par Goten Voir le message
    Pour continuer l'argument de arzar je dirais que justement si tu veux faire des delete etc, en gros géré la mémoire alors autant faire du C.
    Ouais, si j'étais tordu, je te dirais bien que si tu ne veux pas gérer la mémoire, pourquoi ne pas faire du Java ou du C#.... Et si les pointeurs t'effraient, le VB, peut être?

    Sérieusement, tu peux vouloir faire du C++ (ou ajouter du C++ à ton C) pour plein de raisons,

    1- parce que tu peux utiliser la STL pour toutes les petites structures qui n'ont pas besoin d'aller vite
    2- parce que la STL c'est des algorithmes tous prets
    3- parce que les iostream
    4- parce que les classes et un controle de types un peu plus sérieux
    5- parce que les templates c'est mieux que les macros
    6- parce que la librairie graphique que tu utilises l'impose
    7- parce que ton compilateur te le permet...

    Sans pour autant devoir absolument abandonner tes pointeurs, et la possibilité de faire des delete dès que tu n'as plus besoin de la mémoire, ni avoir à te méfier du fait que la STL initialise tout ce qu'elle touche. Si tu traites de gros volumes, c'est en général crucial.

    Citation Envoyé par Goten Voir le message
    En C++ on a tous ce qui faut pour encapsuler ces comportements et éviter de courir à la catastrophe (ou pas d'ailleurs, après tout avec de la rigueur ça passe.) alors pourquoi s'en priver?
    Mais c'est quoi cette catastrophe annoncée? Je veux dire, quelqu'un qui ne sait pas ce qu'est un pointeur, ou qui n'est pas capable de faire un new sans se planter, il faut qu'il fasse plombier, pas informaticien (ou alors du VB...).

    Perso, les erreurs de mémoire sont généralement les trucs les plus faciles à débuguer, y'a plein d'outils pour ca. Les vrais bugs, il ne sont pas là.

    Par ailleurs, il ne s'agit pas de se priver d'utiliser les vector<> (comme tout le monde, mon code en est truffé), juste d'en finir avec l'idée que les new, les delete ou les pointeurs rendent sourd... (ou font pousser les poils du nez)

    Citation Envoyé par Goten Voir le message
    On peut rajouter l'exemple de la désallocation dans le cas d'une exception qui même simple t'obliges à gérer deux fois la libération de la donnée.. (que ça soit un lock, de la mémoire, un fichier etc).
    Oui, mais dans le cas de tableaux de types natifs (99% de mes new) ceci n'arrive à peu près jamais... En revanche, les vecteurs ont la mauvaise habitude de s'initialiser et de se copier tout le temps. Si tu veux faire les choses sérieusement, il va aussi falloir écrire du code pour éviter cela. En fait, tu ne gagneras jamais sur tous les tableaux.

    Ce que je veux dire, c'est que se priver des new et des delete parce que "c'est dangereux" n'est pas raisonnable. D'abord, ce n'est pas vraiment dangereux: si tu codes mal, les vector ne te sauveront pas de la catastrophe. Et si tu ne débugues pas ton code, de toutes façons les utilisateurs auront tes bugs (vector ou pas). Ensuite, parce que dans un grand nombre de cas (genre t'as besoin d'un gros tableau d'entiers ou de POD dont tu connais la taille à l'exécution, pour un calcul, et tu veux le libérer à la fin du calcul) il n'y en a pas, de danger.

    Une fois de plus, presque tous les défenseurs des new et des delete utilisent les vector, et la STL, je ne comprends pas pourquoi il faudrait se priver d'outils simples, pratiques et de bas niveau...

    Francois

  10. #10
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par Arzar Voir le message
    Bizarrement, je préfère écrire :
    Oui oui, moi aussi, mais je reste persuadé que


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    int *tab=new int[taille];
    for(int i=0;i<taille;i++) tab[i]=i;
    f(tab);
    delete[] tab;
    est plus propre, que

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    std::vector<int> tab(taille); 
    // allez une initialisation inutile... 
    std::iota(tab.begin(),tab.end()); 
    // je t'épargne le code avec generate, plus crade encore
    f(&tab[0]);
    // ben oui, la fonction f, système ou librairie externe, elle veut un pointeur
    /* et pis là, t'as intérêt à ne pas avoir besoin de la mémoire, 
        parce que tab reste alloué, ou alors faut aimer un code 
       de 100 000 lignes, coupé en 10 000 fonctions de 10 lignes... */
    Sérieusement, l'argument de fainéantise ne veut pas dire grand chose ici. Pour les map, les list, pas de pb, pour les vectors, bah !

    Francois

  11. #11
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Citation Envoyé par fcharton Voir le message
    Ouais, si j'étais tordu, je te dirais bien que si tu ne veux pas gérer la mémoire, pourquoi ne pas faire du Java ou du C#.... Et si les pointeurs t'effraient, le VB, peut être?
    Rooh de suite les mots qui fâche. (36 éme degrés ). Encore une fois on parle pas de ne pas gérer la mémoire, mais de la gérer d'une autre manière et plus haut niveau.
    Bon et aussi pour casser une autre idée, non les pointeurs ne m'effraient pas. Quand j'ai commencé le C++ (oui j'ai commencé directement sans la case C) alors oui j'évitais les pointeurs comme la peste, depuis j'ai appris à m'en servir. De plus entre temps je me suis mis au C (pour l'os dev notamment) et donc j'ai vraiment du me mettre à la gestion de la mémoire et des pointeurs. Donc non maintenant un code qui contient des pointeurs ne me dérange pas outre mesure quand à sa compréhension et à son utilisation.
    De plus oui les pointeurs sont bénéfiques mais maintenant j'ai tendance à les utiliser encapsulé dans des structures de plus haut niveau qui véhicule plus de sens et gèrent correctement leur durée de vie. J'aime bien citer ce que dis Scott meyers à ses étudiants à ce sujet d'ailleurs :

    "- POINTERS ARE YOUR ENEMIES, because they lead to the kinds of problems that auto_ptr is designed to eliminate.

    Puis:

    "- POINTERS ARE YOUR FRIENDS, because operations on pointers can't throw.

    "Then I tell them to have a nice day :-)
    Et Sutter d'expliciter [1] :
    - USE POINTERS BECAUSE THEY ARE YOUR FRIENDS, because operations on pointers can't throw.

    - KEEP THEM FRIENDLY BY WRAPPING THEM IN MANAGER OBJECTS like auto_ptrs, because this guarantees cleanup. This doesn't compromise the nonthrowing advantages of pointers because auto_ptr operations never throw either (and you can always get at the real pointer inside an auto_ptr whenever you need to).
    Je trouve que ça résume bien l'idée que j'ai en tête quand je programme avec des pointeurs. A noter que quand même tant que je peux et que j'y suis pas forcé je passe par référence (ou par copie dans certains cas).

    Citation Envoyé par fcharton Voir le message
    Mais c'est quoi cette catastrophe annoncée? Je veux dire, quelqu'un qui ne sait pas ce qu'est un pointeur, ou qui n'est pas capable de faire un new sans se planter, il faut qu'il fasse plombier, pas informaticien (ou alors du VB...).


    Perso, les erreurs de mémoire sont généralement les trucs les plus faciles à débuguer, y'a plein d'outils pour ca. Les vrais bugs, il ne sont pas là.
    Hum je suis pas d'accord, un segfault donnant lieu à un UB c'est pas des faciles à débogué car justement ça peut ne pas arriver. Et si on soit la loi de murphy y'a toute les chances pour que ça arrive lors d'une présentation. En effet un new sans se planter c'est pas surhumain mais dans un programme qui devient conséquent tu t'exposes à d'autres risques (double delete etc) mais qui restent eux aussi gérable.

    Enfin je te ferais remarquer que j'ai nuancé en disant catastrophe ... ou non. Ce qui montre bien que je réfute pas du tout ce que tu dis, je dis juste qu'il y'a d'autres maniéres de faire.


    Citation Envoyé par fcharton Voir le message
    Une fois de plus, presque tous les défenseurs des new et des delete utilisent les vector, et la STL...
    Au vue de ce qui est enseigner et des codes qu'on peut voir ici, tu n'as pas tout a fait raison...



    [1]: http://www.gotw.ca/gotw/059.htm
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  12. #12
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par Goten Voir le message
    Je trouve que ça résume bien l'idée que j'ai en tête quand je programme avec des pointeurs.

    A noter que quand même tant que je peux et que j'y suis pas forcé je passe par référence (ou par copie dans certains cas).
    Je suis assez d'accord là dessus. En fait, il faut savoir quand ne pas tenter le diable, en utilisant des pointeurs dans un code qu'on va avoir beaucoup de mal à débuguer. Mais je pense qu'il faut aussi savoir quand les utiliser, quand un code ne présente aucun risque...

    Je crois qu'on programme un peu pareil, en fait...

    Citation Envoyé par Goten Voir le message
    Hum je suis pas d'accord, un segfault donnant lieu à un UB c'est pas des faciles à débogué car justement ça peut ne pas arriver.
    Je ne sais pas, mon débogueur vient avec un truc qui fait ca (chez borland, CodeGuard), en gros, tu refais ce qui plante et vlan, quelques instructions avant, il te dit où tu accèdes à de la mémoire "imprévue". Une segfault, c'est sur que ca fait sale, mais ca se corrige vite. Je n'ai aucun complexe à en avoir, même en présentation, je sais répondre dans l'heure (et mes clients le savent).

    En revanche, le truc sournois qui renvoie une valeur manifestement fausse sans rien faire planter, là, en réunion, je deviens tout pâle...

    Citation Envoyé par Goten Voir le message
    Au vue de ce qui est enseigner et des codes qu'on peut voir ici, tu n'as pas tout a fait raison...
    Là encore, si j'étais tordu, je te dirais que si on regardait le code qui tourne dans des applis professionnellement utilisées aujourd'hui, je crois que mes idées gagneraient... Mais ce ne sont pas des arguments recevables, hein?

    Francois

  13. #13
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Citation Envoyé par fcharton Voir le message
    Je ne sais pas, mon débogueur vient avec un truc qui fait ca (chez borland, CodeGuard), en gros, tu refais ce qui plante et vlan, quelques instructions avant, il te dit où tu accèdes à de la mémoire "imprévue". Une segfault, c'est sur que ca fait sale, mais ca se corrige vite. Je n'ai aucun complexe à en avoir, même en présentation, je sais répondre dans l'heure (et mes clients le savent).
    Oui mais pour ça il faut que tu est une conséquence la première fois de cette segfault. Ce que je veux dire par là c'est que tu peux lancer 20 fois l'appli sans que ça râles et la 21éme fois avoir un rapport d'erreur, et là tu pourras traquer la segfault (que ça soit avec valgrind ou tout autre outil, le but reste le même). C'est la définition d'un comportement indéfinie.

    Citation Envoyé par fcharton Voir le message
    Là encore, si j'étais tordu, je te dirais que si on regardait le code qui tourne dans des applis professionnellement utilisées aujourd'hui, je crois que mes idées gagneraient... Mais ce ne sont pas des arguments recevables, hein?
    Il semblerait qu'en entreprise 60% (le chiffre est totalement empirique, et à valeur d'ordre d'idée) le code C++ est en fait un C/C++ qui tient plus du C with classes qu'autre chose. (le genre de chose que les intervenants de se forum essaye d'éradiquer :p ). Pour ce qui est de l'enseignement, si la SL est vue c'est dans une fin de cours et plus comme une ouverture qu'autre chose.
    L'argument reste recevable, car quand c'est son métier il faut savoir s'adapter au code de l'entreprise (même si on est radicalement contre ce genre de code) plutôt que de vouloir tout révolutionner.
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par fcharton Voir le message
    Je suis assez d'accord là dessus. En fait, il faut savoir quand ne pas tenter le diable, en utilisant des pointeurs dans un code qu'on va avoir beaucoup de mal à débuguer. Mais je pense qu'il faut aussi savoir quand les utiliser, quand un code ne présente aucun risque...

    Je crois qu'on programme un peu pareil, en fait...
    Je crois que nous sommes tous d'accord sur ce point...

    Mais il ne faut pas oublier non plus la question d'origine, et ce qu'elle implique (ou du moins, celle qui a provoqué cette "digression"):
    Pour commencer, je suis novice en C++, j'ai lu des cours en C mais n'ai jamais tenu de réel projet, manque d'idée. Cependant, ayant assez de connaissances en C, je me suis intéressé au C++ près d'un an après et me voici aux structures de données.
    J'ai effectivement zapé la libération de mémoire !
    Dans le code je ne sais pas comment la libèrer... peux-tu m'aider ?
    Nous sommes, visiblement, en présence d'un débutant (qui s'affirme comme tel) qui n'est pas particulièrement à l'aise avec la gestion dynamique de la mémoire...

    Le meilleur moyen de l'aider et de lui permettre d'évoluer est de tirer un signal d'alarme proche de
    Attention, qui dit gestion dynamique de la mémoire implique de savoir précisément ce qu'on fait.
    Le tout en sachant qu'il sera encore bien temps de s'y intéresser lorsqu'il rencontrera une situation dans laquelle il n'aura pas d'autre choix (et de préférence quand même avant d'aller rencontrer son employeur )

    Et, s'il commence à voir les structures, on peut d'ailleurs estimer que ce genre de situations apparaitra finalement très vite, car, juste après les structures, on voit... l'héritage pointer le bout du nez, avec son pendant: le polymorphisme...

    Il n'est donc ici question que de "remettre à plus tard" que quelque chose qui sera à aborder "pas si longtemps plus tard que ça", mais de nous donner l'occasion de nous intéresser au vrai problème plutôt qu'à des problèmes finalement connexes .
    Je ne sais pas, mon débogueur vient avec un truc qui fait ca (chez borland, CodeGuard), en gros, tu refais ce qui plante et vlan, quelques instructions avant, il te dit où tu accèdes à de la mémoire "imprévue". Une segfault, c'est sur que ca fait sale, mais ca se corrige vite. Je n'ai aucun complexe à en avoir, même en présentation, je sais répondre dans l'heure (et mes clients le savent).

    En revanche, le truc sournois qui renvoie une valeur manifestement fausse sans rien faire planter, là, en réunion, je deviens tout pâle...
    Le problème reste toujours le même...

    Que ce soit dans une situation ou dans l'autre, tu te heurte à un comportement indéfini, qui, par définition, lorsqu'il survient, peut avoir une conséquence... ou non (si déjà il survient).

    De plus tu "as la chance" de disposer d'un outil te permettant d'apporter "facilement" une réponse à l'une des situation (mais qui, visiblement, ne t'est d'aucune aide dans l'autre)...

    Et, bien que je ne veuille absolument pas sous entendre que tu aies tord d'une manière ou d'une autre de t'en remettre à cet outil, je voudrais quand même attirer ton attention sur le fait que tu pourrais te retrouver dans une situation où... tu n'en disposera pas forcément

    Et, dans cette situation (hypothétique, je te l'accorde), il te sera aussi dur de trouver la raison du premier problème que du deuxième
    Là encore, si j'étais tordu, je te dirais que si on regardait le code qui tourne dans des applis professionnellement utilisées aujourd'hui, je crois que mes idées gagneraient... Mais ce ne sont pas des arguments recevables, hein?

    Francois
    C'est un argument à double tranchant...

    Le bon coté (de ton point de vue) est qu'il insiste sur le fait que nous devons pouvoir nous adapter à l'existant, et être capable de comprendre ce qui est fait, pourquoi et comment plutôt afin de résister à l'envie de "bazarder" des dizaines (de milliers ) de lignes de codes écrites sur (peut etre) 25 ans afin de (mal) refaire les choses "à notre sauce"

    Le bon coté (de mon point de vue) est qu'il m'est facile de te rétorquer que ce n'est pas parce qu'un imbécile se jette du pont que tu dois obligatoirement faire pareil
    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
    Rédacteur

    Avatar de Matthieu Brucher
    Profil pro
    Développeur HPC
    Inscrit en
    Juillet 2005
    Messages
    9 810
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Développeur HPC
    Secteur : Industrie

    Informations forums :
    Inscription : Juillet 2005
    Messages : 9 810
    Points : 20 970
    Points
    20 970
    Par défaut
    Citation Envoyé par fcharton Voir le message
    Oui oui, moi aussi, mais je reste persuadé que


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    int *tab=new int[taille];
    for(int i=0;i<taille;i++) tab[i]=i;
    f(tab);
    delete[] tab;
    est plus propre, que

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    std::vector<int> tab(taille); 
    // allez une initialisation inutile... 
    std::iota(tab.begin(),tab.end()); 
    // je t'épargne le code avec generate, plus crade encore
    f(&tab[0]);
    // ben oui, la fonction f, système ou librairie externe, elle veut un pointeur
    /* et pis là, t'as intérêt à ne pas avoir besoin de la mémoire, 
        parce que tab reste alloué, ou alors faut aimer un code 
       de 100 000 lignes, coupé en 10 000 fonctions de 10 lignes... */
    Sérieusement, l'argument de fainéantise ne veut pas dire grand chose ici. Pour les map, les list, pas de pb, pour les vectors, bah !

    Francois
    Pas d'accord, dans ce cas, tu encapsules ton tableau dans un boost::scoped_array ou un shared_array à la limite. Là, on n'est pas très exception safe

  16. #16
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Citation Envoyé par fcharton Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    std::vector<int> tab(taille); 
    // allez une initialisation inutile...
    Question : Es-tu tombé sur des cas où une telle initialisation avait un véritable impact sur le programme ? Je demande ça, car j'ai lu qu'en D, l'initialisation avait lieu par défaut (même sur un int i; ou l'équivalent local d'un int *p = new int[10]), mais qu'on pouvait localement la désactiver. Et j'ai toujours eu l'impression qu'en C++, le fait que int i; ne définisse pas la valeur de i était un résultat d'un mauvais équilibrage entre perf et sécurité.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  17. #17
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par JolyLoic Voir le message
    Question : Es-tu tombé sur des cas où une telle initialisation avait un véritable impact sur le programme ?
    Avec des int, seulement sur de très gros tableaux, ou quand on a un grand nombre de vecteurs (par exemple une structure arborescente représentée par des "vector de vector". L'exemple typique serait une arborescence dont on te donne d'abord la structure et ensuite les données. Si tu veux préparer tes vectors (avec des resize), tu vas tout initialiser.

    Tu pourrais faire des reserve(), mais alors il te faut garder les informations de taille, et tu devras absolument faire des push_back() pour remplir tes vecteurs (c'est à dire les remplir dans l'ordre croissant de leurs indices). Par ailleurs, le push_back() génère un peu de copie, je crois... Tu peux aussi redéfinir ton conteneur pour éviter l'initialisation (si je me souviens bien, Koenig et Moo ont une jolie discussion à ce sujet), mais bon, tout ca pour ca!

    Avec des types un tout petit peu plus complexes (struct composites, ou juste string) ca devient très visible très vite.

    Je suis certain que tu peux eviter la plupart des initialisations et des copies inutiles, mais cela demande du travail. On est loin de la simplicité enfantine que l'on prête aux vectors...

    Personnellement j'ai deux cas précis en tête: l'un portant sur des données simples mais arborescentes (donc beaucoup de vecteurs), l'autre sur un unique vecteur, mais assez gros. Dans les 2 cas, on parle d'un bon 15% au chargement, pas spectaculaire, mais visible pour l'utilisateur.

    Je ne suis pas absolument certain qu'il ne s'agit que de l'initialisation, mais dans les deux programmes, récrire les structures de données de grande taille, faites en vector<> lors du prototypage, en utilisant des pointeurs classiques une fois que le proto était testé et les algos maitrisés, s'est traduit pas un gain important de vitesse (entre 25 et 35%...). De façon intéressante, ce constat n'est pas vrai pour les algorithmes de la STL, juste les conteneurs...

    Citation Envoyé par JolyLoic Voir le message
    Je demande ça, car j'ai lu qu'en D, l'initialisation avait lieu par défaut (même sur un int i; ou l'équivalent local d'un int *p = new int[10]), mais qu'on pouvait localement la désactiver. Et j'ai toujours eu l'impression qu'en C++, le fait que int i; ne définisse pas la valeur de i était un résultat d'un mauvais équilibrage entre perf et sécurité.
    Je suis un peu d'accord en fait. Je crois qu'à l'origine, le C++ a été voulu comme un langage capable d'etre aussi portable et bas niveau que le C. Dans ce contexte, l'absence d'initialisation par défaut est une bonne idée (il y a des environnements dans lesquels ce serait pénalisant). Maintenant, et compte tenu de l'évolution du C++ vers un langage compilé de haut niveau, essentiellement destiné à l'informatique classique, on est en droit de se poser la question.

    Francois

  18. #18
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Le cas des vector de vector à était traité plusieurs fois ici, personnellement j'aime pas trop avoir des vector sur deux dimensions. De par leur architecture (justement les allocations etc) et les opérations faites dessus je trouve qu'ils sont pas les plus adapté pour être imbriqué les uns dans les autres et les perfs tombent assez rapidement. Donc soit je me débrouille dans une seule dimension avec un indice ou alors je me tourne vers boost.
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  19. #19
    Invité
    Invité(e)
    Par défaut
    Si on utilise des vector<>, n'est il pas difficile d'éviter leur imbrication dès qu'on représente une structure un tant soit peu hiérarchique? Je veux dire quelque chose comme

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    class UnType {
      int Index;
      vector<UnAutreType> Enfants
    };
     
    vector<UnType> MaHierarchie;
    Sinon, cela reviendrait à dire qu'il faut éviter de mettre dans un vecteur tout objet contenant un autre vector, et alors ca limite quand même passablement l'intérêt du truc, non?

    Francois

  20. #20
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Humm je m'étais jamais posé la question sous cette angle. En effet une fois encapsulé je le voyais comme une donnée membre et pas autre chose. D'ailleurs une fois encapsulé le problème n'est pas le même puisqu'on a pas dans un cas général à faire le même type d'accés. Dans ma tête je pensais plus à des :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::vector< std:vector<> >
    par équivalence aux :

    Donc oui sinon une fois encapsulé imbriqué des vector<> devient monnaie courante. Il faudrait que je fasse quelques tests pour pouvoir en parler plus car là j'avoue que ça m'interroge.
    A première vue j'aurais dis que le problème se poser que dans le cas de vector de vector (j'entends par là imbriqué directement et non via un autre type contenant un vector).
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

Discussions similaires

  1. [ETUDES] [CNAM] Temoignages au sujet des UE
    Par pinocchio dans le forum Etudes
    Réponses: 40
    Dernier message: 06/02/2009, 22h03
  2. [JAR]au sujet des fichiers jar
    Par bobo_j dans le forum Général Java
    Réponses: 2
    Dernier message: 17/10/2005, 16h54
  3. Au sujet des mots de passe
    Par FranT dans le forum Langage
    Réponses: 6
    Dernier message: 17/09/2002, 22h16
  4. Au sujet des constantes
    Par FranT dans le forum Langage
    Réponses: 8
    Dernier message: 09/08/2002, 11h03

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