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

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

C++ Discussion :

Pointeurs intelligents vs. pointeurs bruts


Sujet :

C++

  1. #21
    screetch
    Invité(e)
    Par défaut
    pour le rendre thread-safe, certaines libs implémentent le comptage de référence avec un mutex. De la a dire que ca tue les performances...
    Sinon, un pointeur intelligent ne peut pas être mis dans une union en C++

  2. #22
    Membre habitué
    Inscrit en
    Octobre 2010
    Messages
    64
    Détails du profil
    Informations forums :
    Inscription : Octobre 2010
    Messages : 64
    Points : 146
    Points
    146
    Par défaut
    Bonjour,

    Une intervention -mon point de vue - un peu globale sur tout ce qui a ete dit ici:
    . on ne peut pas toujours se passer de pointeurs, intelligent ou pas (conteneurs polymorphiques, besoin des test de validite (NULL), etc.)
    . mon experience personnelle m'a montre que finalement, je n'ai pas souvent besoin de pointeurs intelligents. Tout d'abord, j'utilise peu de pointeurs (preference aux references), et en general, lorsque j'utilise un pointeur, ce dernier ne sort pas d'un scope tres limite, au sein duquel il est facile a gerer (meme plus facile qu'un pointeur intelligent)

    Donc pour resumer, pour moi, l'utilisation des pointeurs intelligent reste marginale, et ne s'avere necessaire que dans le cas bien precis ou on a besoin de rendre visible des entites dont on se sait pas trop comment ni par qui elles vont etre gerees, et dans le cas ou une reference ne convient pas.


    ps: pardon pour les accents, mais je suis sur un clavier italien configure en english-us, et je peux pas le modifier :/

  3. #23
    Membre chevronné
    Avatar de poukill
    Profil pro
    Inscrit en
    Février 2006
    Messages
    2 155
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 2 155
    Points : 2 107
    Points
    2 107
    Par défaut
    Citation Envoyé par el_socio Voir le message
    lorsque j'utilise un pointeur, ce dernier ne sort pas d'un scope tres limite, au sein duquel il est facile a gerer (meme plus facile qu'un pointeur intelligent)
    Même dans un scope limité, c'est pas forcément simple. Exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    void MaClasse::Foo()
    {
        SuperClass* ptr = new SuperClass(5);
     
        function_that_can_throw():
        // other code
     
        delete ptr;
    }
    Rien que dans ce petit exemple, on a l'utilisation d'un pointeur, un peu de code et finalement sa libération.
    On voit bien que si function_that_can_throw lance un exception, on a une fuite mémoire, le destructeur ne sera pas appelé. On va donc être obligé de rajouter un bloc try-catch pour gérer ce malentendu.

    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
    void MaClasse::Foo()
    {
        try 
        {
            SuperClass* ptr = new SuperClass(5);
     
            function_that_can_throw():
            // other code
     
            delete ptr;
        }
        catch
        {
            delete ptr;
        }
    }
    Un pointeur intelligent passe la gestion d'une ressource a une classe dédié, qui la libére automatiquement dans son destructeur. Et ça, c'est bon !

    Le seul boulot du développeur consiste a trouvé le bon pointeur intelligent (ici, scoped_ptr) parmis les quelques uns à sa disposition !

    S'il est vrai que dans quelques cas encore j'utilise des pointeurs bruts, j'utilise à 95 % des pointeurs intelligents, et dans une appli temps-réel embarquée. Peut-être que je perds 1 % en perf, j'en gagne 10 fois plus en robustesse.

  4. #24
    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 el_socio Voir le message
    en general, lorsque j'utilise un pointeur, ce dernier ne sort pas d'un scope tres limite, au sein duquel il est facile a gerer (meme plus facile qu'un pointeur intelligent)

    Même en présence d'exceptions ?

    Ce genre de situation est le domaine de prédilection de boost.scoped_ptr, ou de unique_ptr en c++0x.
    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.

  5. #25
    Membre habitué
    Inscrit en
    Octobre 2010
    Messages
    64
    Détails du profil
    Informations forums :
    Inscription : Octobre 2010
    Messages : 64
    Points : 146
    Points
    146
    Par défaut
    Je n'utilise ques des fonctions qui ne lancent pas d'exceptions!
    Je rigole mais c'est pas loin de la verite. Je travaille dans un cadre ou, generalement:
    1. S'il y a possibilite d'exception, je dois la gerer immediatement et retourner un code d'erreur (donc je n'utilise generalement pas throw).
    2. Les probabilites de lever une exceptions sont tellement faibles qu'elles sont negligeables.

    Je parle de facon generale (effectivement le cas que vous soulignez est pertinent, et est une des exceptions a ce que disais plus haut), et seulement dans mon cas particulier, c'est a dire ma propre experience professionnelle.
    Il faut voir aussi que j'ai une approche plus "ingenieur" que "universitaire" du developpement. C'est a dire que si mon programme est stable, cela me suffit. Je veux dire que je ne vais pas aller demontrer chaque algo, verifier que tout le code est parfait, etc. Ma preuve a moi, pour savoir si mon code est bon, c'est le departement de test qui me la donne lorsque qu'elle valide mon code.
    Dit comme ca, ca peut paraitre "sale", approximatif, et qu'au final mes programmes doivent etre horribles. Mais je vous assure, qu'apres des annees de dev, cette approche vaut bien l'approche "theorique" qui consiste a appliquer des idiomes et des lois universelles. L'essentiel c'est de le faire bien et de savoir ce que l'on fait. Par exemple, le principe d'encapsulation, c'est bien, mais j'ai vu beaucoup de gens en abuser inutilement (perte de temps et d'energie) ou rendre un compte impossible a modifier parce qu'ils n'avaient pas compris ce que c'etait reellement que l'encapsulation. Je me suis tres eloigne du sujet, mais je voulais juste dire que voila, j'ai une approche du dev tres "pragmatique", qui peut paraitre bizarre, mais qui a fait ses preuves.

    Et je ne me suis pas encore penche sur le c++0x (pas eu le temps, et au boulot on est encore avec visual2005).

  6. #26
    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 el_socio Voir le message
    Je n'utilise ques des fonctions qui ne lancent pas d'exceptions!
    Ca, tu ne peux absolument pas en avoir la certitude...

    New lance une std::bad_alloc si l'allocation échoue.

    Cela fait que tout code dans lequel apparait deux fois l'instruction new (y compris de manière indirecte) pourra provoquer une fuite mémoire.

    Le code, finalement assez simple, proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    int ** tab2d= new int *[10];
    for(int i=0;i<10;++i)
        tab2d[i]=new int[10];
    a donc au total 11 chances de lancer une exception (une lors de l'allocation du tableau de pointeurs (ligne 1), et une à chaque allocation de tableaux dans la boucle (ligne 3) ).

    Le seul cas (sur 11 !!!) qui ne risque pas de provoquer de fuite mémoire si std::bad_alloc est lancé, c'est le cas de la ligne 1

    Je rigole mais c'est pas loin de la verite. Je travaille dans un cadre ou, generalement:
    Tu serais sans doute surpris du nombre de fonctions susceptibles de lancer une exception, et des types d'exceptions qu'elles peuvent lancer (ou répercuter suite à l'appel d'une fonction tierce )
    1. S'il y a possibilite d'exception, je dois la gerer immediatement et retourner un code d'erreur (donc je n'utilise generalement pas throw).
    Ca, c'est ce que TOI tu fais...

    Et ce que la STL fait derrière, tu en fais quoi

    Bon, tu es aussi libre de ne pas utiliser la STL, mais, à quel prix
    2. Les probabilites de lever une exceptions sont tellement faibles qu'elles sont negligeables.
    Tu as sans doute oublié quelque mots: "quand tout se passe bien, et qu'il n'y a pas une application merdique qui tourne à coté"

    Comprenons nous: il ne s'agit pas ici de remettre ton travail en cause (je ne me le permettrais pas, ne te connaissant pas suffisamment ), et je suis près à te faire confiance quand tu dis que les probabilités que l'origine d'une exception se situe dans ton code sont négligeables.

    Par contre, tu me semble très (trop ) confiant dans le travail du voisin ou du développeur de l'application utilisée par lui

    Qu'il suffise qu'un fichier soit utilisé (et "verrouillé") par une application tierce au moment où tu essaye de l'ouvrir, et ton beau château de cartes s'effondre
    Je parle de facon generale (effectivement le cas que vous soulignez est pertinent, et est une des exceptions a ce que disais plus haut), et seulement dans mon cas particulier, c'est a dire ma propre experience professionnelle.
    Comme je l'ai dit, je ne remet ni ta compétence personnelle ni ton expérience en cause, hein
    Il faut voir aussi que j'ai une approche plus "ingenieur" que "universitaire" du developpement. C'est a dire que si mon programme est stable, cela me suffit.
    C'est généralement ce que tout le monde cherche...

    Je ne suis pas non plus universitaire mais...
    Je veux dire que je ne vais pas aller demontrer chaque algo, verifier que tout le code est parfait, etc.
    Je ne le fais que quand il s'agit d'aider quelqu'un à résoudre un problème... provoqué souvent par le code
    Ma preuve a moi, pour savoir si mon code est bon, c'est le departement de test qui me la donne lorsque qu'elle valide mon code.
    Là, il faut être pragmatique...

    Les tests unitaires sont primordiaux, et il est, là non plus, absolument pas question de remettre la compétence ou la qualité du travail du département de test en cause.

    Seulement, le pragmatisme devrait t'inciter à considérer la réussite des tests unitaire pour ce que c'est: la preuve que l'on n'a pas réussi à prendre le programme en faute... non la preuve qu'il ne sera jamais pris en faute :p
    Dit comme ca, ca peut paraitre "sale", approximatif, et qu'au final mes programmes doivent etre horribles.
    Si tu le dis... On va te faire confiance (fallait pas me tendre le bâton pour te frapper )
    Mais je vous assure, qu'apres des annees de dev, cette approche vaut bien l'approche "theorique" qui consiste a appliquer des idiomes et des lois universelles.
    Les idiomes sont là pour te faciliter la vie (parce que d'autres se sont cassé les dents dessus avant toi) et il y a beaucoup de chances pour que tu applique, même sans t'en rendre compte, pas mal de "lois universelles".

    Il est clair que la qualité finale d'un projet dépend énormément de la qualité de la conception et de l'algorithmique.

    Il est clair que l'on peut faire sans les pointeurs intelligents.

    Mais il est aussi clair que faire sans les pointeurs intelligents a un prix, en terme d'attention et d'énergie dépensée.

    Et ce prix sera inversement proportionnel à la compétence des gens avec qui tu travaille.

    Quand tu peux te permettre d'avoir une confiance absolue en la compétence de celui qui travaille à coté de toi, tout va très bien...

    Mais quand tu vois parfois le niveau d'incompétence de certains, il y a de quoi avoir peur
    L'essentiel c'est de le faire bien et de savoir ce que l'on fait.
    Sur ce point, nous sommes tous bien d'accord
    Par exemple, le principe d'encapsulation, c'est bien, mais j'ai vu beaucoup de gens en abuser inutilement (perte de temps et d'energie) ou rendre un compte impossible a modifier parce qu'ils n'avaient pas compris ce que c'etait reellement que l'encapsulation.
    Voilà sans doute la preuve des lignes précédentes

    Tant que tu as affaire à des gens compétents, tout va bien...

    Mais quand ils ne le sont pas...
    Je me suis tres eloigne du sujet, mais je voulais juste dire que voila, j'ai une approche du dev tres "pragmatique", qui peut paraitre bizarre, mais qui a fait ses preuves.
    Parce que tu es dans une situation que l'on pourrait qualifier d'idéale...

    Le fait est que cette situation particulière est (malheureusement) loin d'être la généralité
    Et je ne me suis pas encore penche sur le c++0x (pas eu le temps, et au boulot on est encore avec visual2005).
    Elle n'est de toutes façons pas encore finalisée, et son support reste, même pour les versions récentes, taggé "experimental"...

    Tu auras donc tout le temps de t'y intéresser quand elle sera définitive, avant que son support soit généralisé et envisagé par défaut
    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

  7. #27
    Membre averti
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    301
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 301
    Points : 345
    Points
    345
    Par défaut
    ok pour ce qui est de la gestion de la mémoire mais personnellement les pointeurs nus me servent le plus souvent à gérer mes relations entre objets:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    struct residence
    {};
     
    struct personne
    {
    private:
      residence* maison;
    };
    Et là à part si tout vos objets sont alloués dynamiquement, vous n'allez pas pouvoir gérer le membre maison d'une personne avec un pointeur intelligent.

    Les réferences pourraient être un paliatif mais outre le fait qu'avoir un membre référence interdit alors les constructeurs par défaut, une référence ne permettrait pas le déménagement d'une personne (on ne peut pas rebinder une référence).

    J'ai pas de solution pour me passer dans ce type de cas des pointeurs nus; comment vous procédez sur ce type de cas?

  8. #28
    Débutant
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    688
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2006
    Messages : 688
    Points : 176
    Points
    176
    Par défaut
    dépendends de l'endroit et manière où ton objet résidence est alloué, mais tu peux utiliser weak_ptr

  9. #29
    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 guillaume07 Voir le message
    dépendends de l'endroit et manière où ton objet résidence est alloué, mais tu peux utiliser weak_ptr
    Peut-être un weak_ptr custom, mais surement pas le weak_ptr de boost/du standard, qui n'est prévu pour fonctionner qu'avec des objets tenus par un shared_ptr.
    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.

  10. #30
    Débutant
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    688
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2006
    Messages : 688
    Points : 176
    Points
    176
    Par défaut
    tout à fiat

  11. #31
    Membre régulier

    Inscrit en
    Octobre 2010
    Messages
    50
    Détails du profil
    Informations forums :
    Inscription : Octobre 2010
    Messages : 50
    Points : 70
    Points
    70
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Pour de nombreuses raisons:
    • Les références utilisent une syntaxe identique
    • Les références respectent beaucoup plus facilement la constance
    • Les références force l'existence de l'objet référencé
    • Les pointeurs sont, classiquement, associés à l'allocation dynamique de la mémoire, avec tous les risques que cela peut comporter
    • Toutes celles qui ne me viennent pas forcément en tête à cause de l'heure
    Personellement j'en suis venu à largement abandonner les références.

    Premièrement, la seule raison pour laquelle les références existent est de permettre l'overloading d'opérateurs (lien). L'idée n'a jamais été de fournir une alternative aux pointeurs.

    Ensuite, elles introduisent une deuxième syntaxe pour le même concept. En C, x.y veut dire que x est une structure, x->y veut dire que x réfère (pointe) à une structure. Avec les références, x.y devient ambigu.

    Ensuite, le fait qu'elles ne puissent pas être nulles ni réassignées les rend peu pratiques. Par exemple, si un membre de classe est une référence, il doit être initialisé dans la liste d'initialisation, ce qui est loin d'être toujours faisable. L'opérateur new retourne un pointeur, pas une référence. "this" est un pointeur, pas une référence. Une fonction qui peut retourner un pointeur nul ne peut pas retourner une référence nulle à la place. Elle pourrait lancer une exception, mais ce n'est pas nécessairement une bonne idée (on ne devrait lancer une exception que dans un réel cas d'exception). Ensuite, les librairies C, et même les librairies C++ dans une moindre mesure, utilisent des pointeurs. Tout ceci montre que les références ne sont pas bien intégrées au langage C++ et ne remplacent aucunement les pointeurs. Donc, pourquoi tenter de forcer leur utilisation?

    Je me demande ce que vous voulez dire par "les références respectent beaucoup plus facilement la constance". Une référence est essentiellement un pointeur constant, d'accord, et alors?

    Pour ce qui est des pointeurs intelligents, leur utilité m'apparaît très limitée. Premièrement, il y a le danger de vouloir émuler le langage Java avec un genre de gestion "automatique" de mémoire. Or, deux attrapes: new et delete sont bien plus coûteux en C++ qu'en Java, où l'allocation se fait en incrémentant un simple pointeur, il faut donc éviter les allocations dynamiques en C++ dans la mesure du possible. Ensuite, shared_ptr est un compteur de références qui n'est pas thread-safe: donc, il n'offre aucune garantie dans un environnement multi-threadé, et bonjour les fuites de mémoire.

    Ensuite, il me semble qu'en allouant les ressources dans des constructeurs et en les libérant dans les destructeurs correspondants (patron RAII), on évite tout problème à niveau-là. C'est une des forces de C++ et il faut s'en servir plutôt que d'essayer d'émuler de la gestion de mémoire automatique.

    J'aime bien shared_ptr pour un conteneur d'objets alloués dynamiquement:
    vector<shared_ptr<MyClass> > m_myObjects;
    Mais je ne passe pas le shared_ptr en-dehors de la classe. Qui sait, il pourrait être copié sur un autre thread, et BOUM. Si je dois exposer un de ces objets, j'expose le pointeur brut, tout simplement.

  12. #32
    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
    Salut, et bienvenue sur le forum
    Citation Envoyé par Dr Dédé Voir le message
    Personellement j'en suis venu à largement abandonner les références.
    Et tu prend exactement le contrepied de ce qui se fait de manière générale
    Premièrement, la seule raison pour laquelle les références existent est de permettre l'overloading d'opérateurs (lien). L'idée n'a jamais été de fournir une alternative aux pointeurs.
    Il est vrai que les pointeurs peuvent être utilisés partout où une référence est utilisée, mais il y a de nombreux cas où l'utilisation d'un pointeur n'est pas "ma meilleure solution"...

    De plus, il est facile, avec des pointeurs, de se retrouver face à la nécessité de gérer des pointeurs de pointeurs, voir des pointeur (de pointeurs de ...) de pointeurs , parce que ce que tu gère à la base est déjà un pointeur.
    Ensuite, elles introduisent une deuxième syntaxe pour le même concept. En C, x.y veut dire que x est une structure, x->y veut dire que x réfère (pointe) à une structure. Avec les références, x.y devient ambigu.
    Justement, non...

    La syntaxe est identique à ce que tu utilise lorsque tu manipule une variable "classique"... Ce sont les pointeurs qui introduisent une syntaxe différente à ce que tu utilise pour les variables

    Quelque part, tu prend le problème à l'envers, car le but des pointeurs est, justement, de te permettre de manipuler la variable passée en argument, et non une copie de celle-ci

    Mais, si l'on avait pas le choix (à cause de l'impossibilité "en l'état de la technique" de faire en sorte que l'on puisse utiliser une syntaxe identique) lorsque C a été inventé, l'état des connaissances était déjà tout à fait différent au moment où C++ a été inventé.

    Et, d'une certaine manière, l'approche utilisée par les références de C++ est beaucoup plus logique que celle de l'approche utilisée par les pointeurs, que ce soit en C ou en C++.

    Il n'y a pas de raison à forcer l'utilisation d'une syntaxe différente lorsque c'est pour arriver à un résultat identique

    Ensuite, le fait qu'elles ne puissent pas être nulles ni réassignées les rend peu pratiques. Par exemple, si un membre de classe est une référence, il doit être initialisé dans la liste d'initialisation, ce qui est loin d'être toujours faisable.
    Attention: on ne dit pas qu'il ne faut PAS utiliser les pointeurs, on dit qu'il faut utiliser les référence "chaque fois que faire se peut" et les pointeurs "lorsque l'on n'a pas le choix".

    Je te rappelle aussi que l'on travaille dans un concept orienté objet, c'est à dire dans le cadre de fonctions membres dans une grosse majorité des cas, et que, si pour certains membres d'une classe l'utilisation de pointeurs est effectivement la seule solution, les références sont, essentiellement, utilisées pour transmettre des arguments aux fonctions membres (bien qu'il soit possible d'utiliser une référence comme membre de la classe, et que cela implique alors de respecter certaines conditions)

    De plus, il est parfois (et je dis bien parfois et non toujours) intéressant de forcer le fait qu'un membre soit effectivement défini directement dans le constructeur, car il peut dans certains cas ne pas y avoir de sens à permettre que "l'objet référencé" n'existe pas

    Enfin, il faut être conscient que les constructeur ne prenant pas de paramètres ne sont pas obligatoires pour de nombreux types: il est, d'ailleurs, beaucoup plus fréquent de se trouver dans une situation où un tel constructeur n'a strictement aucun sens que dans une situation dans laquelle il est, effectivement, opportun de le prévoir
    L'opérateur new retourne un pointeur, pas une référence.
    Oui, mais l'opérateur new en C++ n'a rien à voir avec les fonctions *alloc de C.

    L'opérateur new est, classiquement, utilisé dans deux cas:

    1. Quand on veut gérer soi même la durée de vie d'un objet, et, dans ce cas, il reste toujours intéressant de faire en sorte que cette durée de vie soi gérée par un objet bien précis, et donc de profiter justement des pointeurs intelligents pour que tous les types qui doivent utiliser le pointeur sans en gérer la durée de vie de l'objet alloué dynamiquement puissent être prévenu de la destruction de ce dernier
    2. Quand il s'agit de travailler de manière polymorphe, c'est à dire, créer un objet de type dérivé en le faisant passer pour une instance du type de base.

    "this" est un pointeur, pas une référence.
    Oui, mais les cas où il est absolument nécessaire d'utiliser le pointeur this se comptent sur les doigts d'une main...

    Son utilisation étant implicite au niveau des fonctions membres, je crois que je l'utilise dans... une fonction membre sur cent, en moyenne (même si je n'ai jamais fait de statistiques sur le sujet )
    Une fonction qui peut retourner un pointeur nul ne peut pas retourner une référence nulle à la place.
    C'est, effectivement, la grosse différence entre les références et les pointeurs: les références forcent l'objet référencé à exister

    Mais, alors que tu semble considérer que c'est un problème, c'est, dans la plupart des cas une sécurité supplémentaire, et ce, pour deux raisons:

    Lorsque tu transmet une variable par argument, tu évites le test if(ptr!=NULL) que tu es obligé de placer pour un pointeur (sous peine de de te retrouver avec une belle erreur de segmentation). Cela n'a l'air de rien, mais, si tu dois placer ce test (et cela arrive beaucoup plus souvent que l'on ne peut le croire de prime abord ) dans une logique exécutée de (très) nombreuses fois en boucle, même si ce test ne prend que "quelques fréquences d'horloges", cela peut facilement mener à un plombage des performances globales.

    La deuxième raison est qu'il arrive, justement, beaucoup plus souvent que tu te retrouve dans une situation dans laquelle l'objet passé en argument doive exister que dans une situation dans laquelle il peut ne pas exister.

    Or le passage par référence (en réalité, je parle ici du passage par référence constante ) permet la création de "variable temporaire anonymes", c'est à dire de variables qui n'existent absolument pas dans la fonction appelantes, et qui sont créée exclusivement pour la durée de la fonction appelée (celle qui a besoin de l'objet comme paramètre).

    Le passage par pointeur ne permet pas une telle possibilité

    Or, C++ est très loin de la philosophie de C tel qu'il était à ses débuts (je crois que cela a changé avec la dernière norme, mais je n'en suis même pas tout à fait sur ) car on considère comme préférable de ne créer les différentes variable que "lorsque l'on en a absolument besoin", alors que les premières normes de C t'obligeaint à... déclarer l'ensemble des variables que tu va manipuler dans une fonction au début de celle-ci.
    Elle pourrait lancer une exception
    Tout à fait, mais
    , mais ce n'est pas nécessairement une bonne idée (on ne devrait lancer une exception que dans un réel cas d'exception).
    Là dessus, je ne suis pas d'accord.

    Il y a, effectivement, une école qui considère cela, mais une exception peut parfaitement être lancée exactement partout où un retour d'erreur serait utilisé, et, plus particulièrement, cela s'avère des plus intéressant dans les situations où la solution à une erreur ne peut pas être apportée que dans une fonction autre que celle qui appelle directement une fonction susceptible de provoquer une erreur.

    Une exception permet, simplement, de signaler que, à un moment donné, le système se retrouve dans un état incohérent (ou que l'on a déterminé que, si on allait plus loin dans la logique, on finira par se trouver dans une telle situation).

    Le fait est que la raison d'origine qui peut faire que l'on finira avec un système dans un état incohérent peut se trouver dans une fonction qui appelle une fonction qui appelle une fonction ( ... ) qui appelle la fonction dans laquelle on se rend compte que le système est dans un état incohérent, et que, bien souvent, la solution ne pourra être apportée que... dans la fonction dans laquelle se trouve l'origine de l'erreur.

    Le fait est que, si tu manipule des retour d'erreur (il y a d'ailleurs une discussion récente sur le sujet), tu sera obligé de prendre au minimum le retour d'erreur dans chaque fonction appelée entre celle dans laquelle se trouve l'origine de l'erreur et celle dans laquelle on se rend compte que l'erreur survient, sans *forcément* être en mesure dy faire quoi que ce soit, alors que tu ne dois récupérer une exception que lorsque... tu peux au minimum apporter une partie de la solution à l'erreur qui s'est produite
    Ensuite, les librairies C, et même les librairies C++ dans une moindre mesure, utilisent des pointeurs.
    En C, c'est normal, les exceptions n'existent pas...

    Par contre, en C++, on ne doit pas utiliser les même bibliothèques

    Regarde simplement du coté de la STL (qui est, rappelons le, LA bibliothèque fournie par le standard, et donc la bibliothèque la plus susceptible d'être pleinement supportée par n'importe quel compilateur ), et tu remarquera que l'utilisation des références est, pour ainsi dire, PARTOUT (en dehors, peut être de l'implémentation elle-même... mais ca, ce n'est pas notre problème d'utilisateur de la bibliothèque ).

    C'est bien simple: tu remarquera même que, lorsqu'il s'agit de passer un pointeur, on passe généralement... une référence sur un pointeur
    Tout ceci montre que les références ne sont pas bien intégrées au langage C++
    Elles sont, au contraire, parfaitement intégrées au langage...

    Le problème est, surtout, que de nombreuses bibliothèque "soit disant C++" font un horrible amalgame entre le C et le C++, alors que le style de programmation actuel tend à clairement séparer les deux langages, et à utiliser, à l'heure actuelle, les possibilités propres à C++, et non celles issues de C.
    et ne remplacent aucunement les pointeurs.
    C'est ce que tu crois... Elles ne remplacent, effectivement, pas les pointeurs dans l'ensemble des cas, mais elles les remplacent dans, très facilement, 80 à 90% des cas...

    Il n'existe pas de statistiques précises sur le sujet, mais je suis sur que nous arrivons très facilement à de telles proportions
    Donc, pourquoi tenter de forcer leur utilisation?
    Parce qu'elles apportent une sécurisation incroyable par rapport aux pointeurs, tout simplement
    Je me demande ce que vous voulez dire par "les références respectent beaucoup plus facilement la constance". Une référence est essentiellement un pointeur constant, d'accord, et alors?
    Ce n'est pas cela que je veux dire...

    Oui, effectivement, une référence peut être assimilé à un pointeur constant.

    Mais, ce qui se passe surtout, c'est que ce n'est pas parce que tu as un pointeur constant que l'objet pointé par le pointeur sera constant.

    Or, l'une des raisons qui peut t'inciter à utiliser un pointeur est simplement le fait de vouloir éviter la copie de l'objet passé en paramètre (ne serait-ce que parce qu'il n'y a, simplement, aucun sens à permettre la copie de toutes les classes qui on sémantique d'entité ), sans forcément permettre la modification de l'objet d'origine dans la fonction appelée.

    L'avantage des références, c'est que tu as la notion de référence constante, qui a pour but de prévenir le compilateur que "attention, la fonction appelée s'engage à ne pas modifier l'objet passé en paramètre".

    Cela n'a l'air de rien, mais cela intervient dans une approche beaucoup plus globale dont le terme anglais est la "const correctness", que l'on pourrait traduire "le respect de la constance des objets".

    En effet, il est possible, pour les fonctions membres d'une classe de préciser que telle ou telle fonction s'engage à... ne pas modifier l'objet en cours d'utilisation.

    Lorsque tu transmet une référence constante à une fonction, le compilateur refusera que tu invoque n'importe quelle fonction qui n'a pas pris cet engagement.

    Tu as donc la certitude, en passant une référence constante à une fonction, que l'objet d'origine ne sera absolument pas modifié.

    Tu ne peux en aucun cas avoir ce genre de certitude avec un pointeur, même si tu passe un pointeur constant (tu peux tout au plus avoir la certitude que le pointeur ne va pas finir par pointer sur un objet différent de celui d'origine )
    Pour ce qui est des pointeurs intelligents, leur utilité m'apparaît très limitée. Premièrement, il y a le danger de vouloir émuler le langage Java avec un genre de gestion "automatique" de mémoire.
    La gestion automatique de la mémoire que l'on obtient avec les pointeurs intelligents est totalement différente de celle que l'on objet en java.

    On a beau utiliser le même terme, la comparaison s'arrête vraiment

    En C++, les pointeurs intelligents permettent, simplement, de préciser une instance d'objet qui prendra la responsabilité de la libération de la mémoire au moment où cette instance sera détruite, alors que, en java, c'est, pour ainsi dire, un système tout à fait "séparé du langage" qui va décider "plus ou moins à sa guise" qu'il est temps de libérer la mémoire allouée à un objet donné.

    Ne confondons pas les torchons et les serpillères, s'il te plait
    Or, deux attrapes: new et delete sont bien plus coûteux en C++ qu'en Java, où l'allocation se fait en incrémentant un simple pointeur, il faut donc éviter les allocations dynamiques en C++ dans la mesure du possible.
    Cela, je suis tout à fait d'accord avec toi, et j'ai d'ailleurs parlé de ce problème quelques lignes plus haut
    Ensuite, shared_ptr est un compteur de références qui n'est pas thread-safe: donc, il n'offre aucune garantie dans un environnement multi-threadé, et bonjour les fuites de mémoire.
    Mais il est très facile de le rendre thread-safe, même si cela passe par un double test

    De plus, les pointeurs intelligents sont loin de se limiter aux shared_ptr...

    Si tu t'intéressait un peu d'avantage aux pointeurs intelligents, tu remarquerais que tu as un panel des plus sympa, et des cas d'utilisation qui te permettent d'éviter pas mal de soucis, y compris dans un contexte multi thread
    Ensuite, il me semble qu'en allouant les ressources dans des constructeurs et en les libérant dans les destructeurs correspondants (patron RAII), on évite tout problème à niveau-là. C'est une des forces de C++ et il faut s'en servir plutôt que d'essayer d'émuler de la gestion de mémoire automatique.
    RAII apporte certaines solutions aux problèmes de l'allocation dynamique de la mémoire, mais ne résout pas tout...

    L'exemple le plus simple qui t'en montre les limites est celui que j'ai présenté quelques interventions plus haut:
    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
    Type ** foo(int dim1, int dim2)
    {
        /* new lance une exception de type bad_alloc si l'allocation mémoire
         * échoue...
         * cette ligne ne posera donc pas de problème si elle échoue
         */
        Type ** tab= new Type * [dim1]; 
        /* par contre, la boucle qui suit peut, à chaque exécution, également
         * lancer une exception de type bad_alloc
         * 
         * Si cela arrive, tu auras une fuite mémoire correspondant à
         *  l'ensemble de la mémoire
         * - allouée lors de la ligne ci-dessus +
         * - celle allouée lors de chaque exécution "réussie" de la boucle
         * 
         * Le problème sera alors que bad_alloc peut, aussi bien, être lancée
         * pour i = 0 que... pour i = dim2-1
         */
        for(int i=0; i<dim1;++i)
            tab[i] = new Type[dim2]:
     
    }
    Il est, tout à fait, possible de contourner le problème, je ne dis pas le contraire, mais un pointeur intelligent te permettra d'être sur que, si une allocation dynamique (quelle qu'elle soit) échoue, l'ensemble de la mémoire allouée lors des allocations qui ont réussi sera libérée, le tout, sans que tu n'aie à t'inquiéter de gérer ce cas particulier manuellement.

    Cela t'apporte, à la fois une sécurité supplémentaire (dans le sens où cela t'évite une fuite mémoire), mais, aussi, une simplicité qui te permet de te concentrer sur ce qui est *réellement* important, et non ce qui n'est, en définitive, qu'un "détail d'implémentation"
    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

  13. #33
    Membre régulier

    Inscrit en
    Octobre 2010
    Messages
    50
    Détails du profil
    Informations forums :
    Inscription : Octobre 2010
    Messages : 50
    Points : 70
    Points
    70
    Par défaut
    Salut koala,

    j'ai argumenté que les références introduisent une ambiguïté par rapport au C. En effet, en C,

    x.y

    veut dire que x est une structure (la structure elle-même). En C++, il y a deux options: soit x est un objet, soit une référence. Deux sémantiques, une seule syntaxe, donc, ambiguïté. Je ne vois pas comment on peut le nier. Tu me réponds que la syntaxe est identique... Mais c'est justement parce que la syntaxe est identique qu'il y a ambiguïté.

    De plus, il est facile, avec des pointeurs, de se retrouver face à la nécessité de gérer des pointeurs de pointeurs, voir des pointeur (de pointeurs de ...) de pointeurs , parce que ce que tu gère à la base est déjà un pointeur.
    En effet, c'est plus léger syntaxiquement. C'est un avantage indéniable des références. Et pourtant, reste que c'est une duplication de fonctionalité et que cela introduit une ambiguïté syntaxique (unique au C++ d'ailleurs).

    Je suis au courant de ce que tu mentionnes au sujet des constructeurs, de new, de this, etc. Je ne faisais qu'illustrer que si les références étaient réellement sensées remplacer les pointeurs "dans la mesure du possible", elles auraient été implémentées différemment. "this" aurait été une référence, "new" aurait retourné une référence, la librairie standard C++ n'utiliserait que des références, la librairie standard C serait entièrement réimplémentée en termes de références, etc.

    Vu que 1) la seule utilisation obligatoire des références dans le langage est la définition des surcharges d'opérateurs et des constructeurs de copie, et 2) les contraintes d'utilisations les rendent inutilisables dans bien des cas (pas de réassignation et pas de nullité), ça m'apparaît plutôt un ajout tardif et sans grande importance. Je suis plutôt d'avis que les pointeurs restent la façon standard de "référencer".

    Par contre, en C++, on ne doit pas utiliser les même bibliothèques
    Il ne faut pas chercher bien loin pourtant. Par exemple, les streams standards utilisent les pointeurs pour les fonctions read et write. Pas moyen de passer une string& ou quoique ce soit d'autre.

    istream& read ( char* s, streamsize n );
    ostream& write ( const char* s , streamsize n );

    Ou encore, la STL est composée de trois choses: des conteneurs, des algorithmes et des itérateurs. Or les itérateurs sont implémentées comme des pointeurs: ils peuvent être déréférencés, il peuvent être incrémentés, et ils peuvent pointer sur "rien" (sémantiquement).

    Donc, les pointeurs font partie intégrante de la librairie standard C++.

    Or le passage par référence (en réalité, je parle ici du passage par référence constante ) permet la création de "variable temporaire anonymes", c'est à dire de variables qui n'existent absolument pas dans la fonction appelantes, et qui sont créée exclusivement pour la durée de la fonction appelée (celle qui a besoin de l'objet comme paramètre).
    Et ça ne marche qu'avec une référence constante, donc l'objet sera non-modifiable. Et si je veux le modifier? J'en reviens encore à utiliser un pointeur.

    De toute façon, ce n'est qu'un raccourci syntaxique; l'objet en question doit bien exister quelque part pour être passé en paramètre, et il va en fait être sur la pile comme tous les autres.

    Donc, je te concède encore une fois que les références permettent des raccourcis syntaxiques intéressants dans certains cas. Mais dans mon expérience, ces cas sont loin de former la majorité et il arrive si souvent qu'on doivent changer une référence pour un pointeur que l'on est mieux de toujours utiliser des pointeurs, simplifiant la syntaxe (plus lourde oui mais plus cohérente et explicite) et d'éviter ainsi de perdre du temps.

    La deuxième raison est qu'il arrive, justement, beaucoup plus souvent que tu te retrouve dans une situation dans laquelle l'objet passé en argument doive exister que dans une situation dans laquelle il peut ne pas exister.
    Or le concept d'une "référence qui ne peut pas être nulle" n'existe pas dans la plupart des langages: Java, C#, Python, etc. Et pourtant on peut garantir qu'une fonction ne retournera pas nul dans ces langages: en lançant une exception en cas d'erreur. La même chose est possible en C++, et il n'y a donc pas besoin d'une "référence qui ne peut pas être nulle". Je peux retourner un pointeur si tout va bien, et lancer une exception en cas d'erreur. Ma fonction garantit donc que le pointeur sera non-nul. Le fait d'utiliser une référence ici est sans aucune utilité! Si j'utilise une référence, le code de la fonction sera le même à 2 caractères près (&*), et la sémantique de la fonction sera exactement la même.

    Or, C++ est très loin de la philosophie de C tel qu'il était à ses débuts (je crois que cela a changé avec la dernière norme, mais je n'en suis même pas tout à fait sur ) car on considère comme préférable de ne créer les différentes variable que "lorsque l'on en a absolument besoin", alors que les premières normes de C t'obligeaint à... déclarer l'ensemble des variables que tu va manipuler dans une fonction au début de celle-ci.
    En fait nous sommes en désaccord fondamental ici. C++ sert les mêmes buts que le C, à plus grande échelle. Il est essentiellement implémenté comme un superset du C. C++ est un langage de gestion manuelle de mémoire et la syntaxe des pointeurs du C est parfaitement adaptée à cette tâche. (D'ailleurs C# l'a repris pour le code "unsafe".) Les références ne servent qu'à permettre certains raccourcis syntaxiques et à donner l'impression que C++ est plus orienté-objet qu'il ne l'est, au prix d'une duplication de fonctionnalités, de l'introduction d'une ambiguïté syntaxique, et de discussions comme celle-ci.

    Il y a, effectivement, une école qui considère cela, mais une exception peut parfaitement être lancée exactement partout où un retour d'erreur serait utilisé
    Nous sommes d'accord. Je ne fais pas de différence entre "un cas d'exception" et "une erreur". Une exception peut être utilisée partout où on veut retourner une erreur.

    Parfois, cependant, on peut très bien vouloir retourner nul sans que cela représente une erreur. Par exemple, une fonction de recherche ne devrait pas lancer d'exception si elle ne trouve pas l'objet voulu, mais bien retourner nul. C'est ce que fait std::find d'ailleurs; mais comme std::find retourne une référence, il se voit contraint de retourner une référence sur un "objet invalide" (l'itérateur end() ou npos), et là on nage en plein ridicule, puisqu'un pointeur ferait exactement la même chose de façon beaucoup plus simple.

    (je répondrai plus tard pour les pointeurs intelligents)

  14. #34
    Membre régulier

    Inscrit en
    Octobre 2010
    Messages
    50
    Détails du profil
    Informations forums :
    Inscription : Octobre 2010
    Messages : 50
    Points : 70
    Points
    70
    Par défaut
    La gestion automatique de la mémoire que l'on obtient avec les pointeurs intelligents est totalement différente de celle que l'on objet en java.

    On a beau utiliser le même terme, la comparaison s'arrête vraiment là

    En C++, les pointeurs intelligents permettent, simplement, de préciser une instance d'objet qui prendra la responsabilité de la libération de la mémoire au moment où cette instance sera détruite, alors que, en java, c'est, pour ainsi dire, un système tout à fait "séparé du langage" qui va décider "plus ou moins à sa guise" qu'il est temps de libérer la mémoire allouée à un objet donné.

    Ne confondons pas les torchons et les serpillères, s'il te plait
    Je suis entièrement d'accord, et comme tu comprends cette différence tu as de bonnes chances d'utiliser correctement les pointeurs intelligents. Mais j'ai trop souvent entendu dire qu'ils permettaient d'abstraire la gestion de mémoire comme en Java, alors que c'est tout à fait faux. C'est un outil qui aide dans certains cas mais qui n'offre aucunement les caractéristiques de performance et de sécurité de Java. En C++ la seule entité qui peut gérer correctement la mémoire, c'est le programmeur, et s'il ne comprend pas comment les outils qu'il utilise fonctionnent, c'est le désastre assuré.

    Si tu t'intéressait un peu d'avantage aux pointeurs intelligents, tu remarquerais que tu as un panel des plus sympa, et des cas d'utilisation qui te permettent d'éviter pas mal de soucis, y compris dans un contexte multi thread
    Je ne suis pas au courant, je connais boost::shared_ptr, scoped_ptr, weak_ptr et std::auto_ptr. Aucun n'est thread-safe. Tu as des exemples?

    RAII apporte certaines solutions aux problèmes de l'allocation dynamique de la mémoire, mais ne résout pas tout...

    L'exemple le plus simple qui t'en montre les limites est celui que j'ai présenté quelques interventions plus haut:
    Code :
    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
    Type ** foo(int dim1, int dim2)
    {
        /* new lance une exception de type bad_alloc si l'allocation mémoire
         * échoue...
         * cette ligne ne posera donc pas de problème si elle échoue
         */
        Type ** tab= new Type * [dim1]; 
        /* par contre, la boucle qui suit peut, à chaque exécution, également
         * lancer une exception de type bad_alloc
         * 
         * Si cela arrive, tu auras une fuite mémoire correspondant à
         *  l'ensemble de la mémoire
         * - allouée lors de la ligne ci-dessus +
         * - celle allouée lors de chaque exécution "réussie" de la boucle
         * 
         * Le problème sera alors que bad_alloc peut, aussi bien, être lancée
         * pour i = 0 que... pour i = dim2-1
         */
        for(int i=0; i<dim1;++i)
            tab[i] = new Type[dim2]:
     
    }
    Il est, tout à fait, possible de contourner le problème, je ne dis pas le contraire, mais un pointeur intelligent te permettra d'être sur que, si une allocation dynamique (quelle qu'elle soit) échoue, l'ensemble de la mémoire allouée lors des allocations qui ont réussi sera libérée, le tout, sans que tu n'aie à t'inquiéter de gérer ce cas particulier manuellement.
    En fait, tu utilises ici un pointeur intelligent comme un objet RAII: il alloue la mémoire lors de sa construction, et il la libère lors de sa destruction. Et nous sommes d'accord là-dessus, j'utilise souvent des pointeurs intelligents dans des situations similaires.

  15. #35
    Membre régulier

    Inscrit en
    Octobre 2010
    Messages
    50
    Détails du profil
    Informations forums :
    Inscription : Octobre 2010
    Messages : 50
    Points : 70
    Points
    70
    Par défaut
    Un dernier point (après ça j'arrête, promis):

    Lorsque tu transmet une référence constante à une fonction, le compilateur refusera que tu invoque n'importe quelle fonction qui n'a pas pris cet engagement.

    Tu as donc la certitude, en passant une référence constante à une fonction, que l'objet d'origine ne sera absolument pas modifié.

    Tu ne peux en aucun cas avoir ce genre de certitude avec un pointeur, même si tu passe un pointeur constant (tu peux tout au plus avoir la certitude que le pointeur ne va pas finir par pointer sur un objet différent de celui d'origine )
    Tu peux avoir exactement la même "certitude" (on reparlera de const une autre fois ) avec un pointeur:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    struct A { 
    	void NonConstMethod() {}
    	int a; 
    };
     
    int main() {
    	A a;
    	const A& aRef = a;
    	const A* aPtr = &a;
    	aRef.NonConstMethod(); // erreur
    	aPtr->NonConstMethod(); // erreur
    }
    La ligne qui utilise le pointeur sur const A est tout aussi illégale que celle qui utilise la référence sur const A.

  16. #36
    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 Dr Dédé Voir le message
    Je ne suis pas au courant, je connais boost::shared_ptr, scoped_ptr, weak_ptr et std::auto_ptr. Aucun n'est thread-safe. Tu as des exemples?
    std::auto_ptr, peut-être pas (mais d'ailleurs, son rôle étant de ne pas partager, ça ne me gêne pas qu'il ne gère pas le partage entre threads...) (j'ai mis peut-être, car rien n'empêche une implémentation de le rendre thread-safe).

    Idem boost::scoped_ptr.

    Par contre, boost::shared_ptr et weak_ptr, qui eux sont orientés partagé, sont thread-safe. Et actuellement, les versions standard le sont aussi (je dis actuellement car certains voudraient disposer de shared_ptr non thread-safe dans le standard, afin d'éviter tout coût de synchronisation dans les cas où ça n'est pas nécessaire, mais actuellement, le compromis est plutôt de dire : "shared_ptr thread safe, pourquoi pas une autre classe au nom à choisir qui fasse comme shared_ptr mais ne le soit pas".
    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. #37
    Membre régulier

    Inscrit en
    Octobre 2010
    Messages
    50
    Détails du profil
    Informations forums :
    Inscription : Octobre 2010
    Messages : 50
    Points : 70
    Points
    70
    Par défaut
    Merci pour l'info, je me suis renseigné plus sérieusement. (documentation officielle) shared_ptr est mieux que je me le représentais: il est sécuritaire pour des opérations simultanées sur deux instances différentes (qui réfèrent au même objet). Il n'est pas sécuritaire pour des opérations simultanées sur la même instance, toutefois. La synchronisation se fait de façon peu coûteuse puisque la librairie utilise des opérations atomiques spécifiques à chaque plateforme plutôt qu'un bête mutex.

    "shared_ptr thread safe, pourquoi pas une autre classe au nom à choisir qui fasse comme shared_ptr mais ne le soit pas".
    Tu peux définir la macro BOOST_SP_DISABLE_THREADS pour ce faire.

  18. #38
    Membre habitué
    Inscrit en
    Octobre 2010
    Messages
    64
    Détails du profil
    Informations forums :
    Inscription : Octobre 2010
    Messages : 64
    Points : 146
    Points
    146
    Par défaut
    Citation Envoyé par koala01
    Citation Envoyé par el_socio
    Je n'utilise ques des fonctions qui ne lancent pas d'exceptions!
    Ca, tu ne peux absolument pas en avoir la certitude...

    New lance une std::bad_alloc si l'allocation échoue.
    C'est la remarque que j'attendais, et pour laquelle j'avais prepare le terrain avec mon histoire sur dev type "ingenieur" vs type "universitaire" (avec tous les guillemets que vous voudrez, car les deux sont intimements interdependants et je ne concois pas l'un sans l'autre).
    En fait, si un new echoue (sur quelque chose qui ne demande rien d'autre que de la memoire), alors:
    1. Je ne vois pas l'interet de gerer cette exception. Si ce new echoue, on ne pourra meme pas allouer une chaine de carractere pour faire un log, et meme un log sans fichier (un log en reseau par exempl).
    2. Aujourd'hui, et dans mon environnement de dev (c'est a dire des programmes destines a fonctionner sur des desktops ou des serveurs) il y a statistiquement moins de chance qu'un new echoue qu'une surtension face griller le disque dur.

  19. #39
    Membre habitué
    Inscrit en
    Octobre 2010
    Messages
    64
    Détails du profil
    Informations forums :
    Inscription : Octobre 2010
    Messages : 64
    Points : 146
    Points
    146
    Par défaut
    Je vois que vous connaissez bien le sujet, alors j'en profite pour vous poser une question. Moi je ne programme que "pour moi-meme", ou au pire, pour des gens qui travaillent a quelques metres de moi. Je veux dire que mon code ne sera jamais utilise par quelqu'un qui n'est pas present dans la meme piece que moi.

    C'est pourquoi je ne sais pas si, dans le cas precis ou on programme une bibliotheque destinee a etre utilisee par d'autres developpeurs, l'utilisation de pointeurs intelligents doit-elle etre systematique pour l'interface publique des classes que l'on met a disposition des utilisateurs?

  20. #40
    Membre chevronné
    Avatar de Joel F
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Septembre 2002
    Messages
    918
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Service public

    Informations forums :
    Inscription : Septembre 2002
    Messages : 918
    Points : 1 921
    Points
    1 921
    Par défaut
    Citation Envoyé par el_socio Voir le message
    En fait, si un new echoue (sur quelque chose qui ne demande rien d'autre que de la memoire), alors:
    1. Je ne vois pas l'interet de gerer cette exception. Si ce new echoue, on ne pourra meme pas allouer une chaine de carractere pour faire un log, et meme un log sans fichier (un log en reseau par exempl).
    sur certains systeme tu as des logs hardware qui gèrent ça très bien.
    On en reviens à la définition primaire du traitement d'erreur:
    * assertion pour precondition violées
    * exception pour post-condtions violées
    La bonne pratique consiste à relever l'erreur là ou elle arrive, la traiter ou la faire suivre à un appelelant mais pas la laisser courir pour pourrir le programme 506577km plus loin.

    Personnelement, je me débrouille toujours pour faire en sorte que sur bad_alloc, l'exception soit catché, le pointeur renvoyé devient NULL et la prochaine fonction utilisant ce poineur ASSERT sur la précondition ptr != NULL.

Discussions similaires

  1. Réponses: 55
    Dernier message: 18/03/2014, 12h11
  2. Réponses: 13
    Dernier message: 27/04/2012, 16h03
  3. Les pointeurs intelligents
    Par MatRem dans le forum C++
    Réponses: 8
    Dernier message: 20/06/2006, 19h27
  4. pointeur intelligent??
    Par yashiro dans le forum C++
    Réponses: 3
    Dernier message: 04/04/2006, 08h08
  5. Pointeur intelligent boost : return NULL ->comment faire?
    Par choinul dans le forum Bibliothèques
    Réponses: 7
    Dernier message: 21/12/2005, 16h24

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