GRATUIT

Vos offres d'emploi informatique

Développeurs, chefs de projets, ingénieurs, informaticiens
Postez gratuitement vos offres d'emploi ici visibles par 4 000 000 de visiteurs uniques par mois

emploi.developpez.com

C++ : un nouveau débat technique lancé par Herb Sutter
Qui relance les questions "Guru of the Week" suite à la sortie de la norme C++11

Le , par gbdivers, Inactif
C'est une source d'information que les anciens connaissent bien. Guru of the Week (GotW) est un site créé et alimenté par Herb Sutter entre 1997 et 2003. Le principe est simple : une question technique est posée et les lecteurs interviennent pour répondre à la question en essayant de faire le tour de toutes les difficultés techniques qui pourraient apparaître. Cette discussion aboutie à une analyse en profondeur de la problématique posée. Ces questions et réponses ont eu tellement de succès que Herb Sutter a publié plusieurs ouvrages pour regrouper et compléter ces analyses : Exceptional C++ et More Exceptional C++.

Ces ouvrages restent très intéressants à lire pour celui qui veut progresser en C++, mais avec la sortie du C++11, il commençait à devenir nécessaire de refaire le point en prenant en compte la nouvelle norme. Depuis peu, Herb Sutter a donc relancé les GotW sur son blog, Sutter’s Mill. La nouvelle série de questions a repris en commençant avec le numéro 100. Ce week-end, Herb Sutter a publié un nouveau GotW numéro 104. Voici un résumé de la problématique qui est soumise :

Voici un code classique que l'on retrouve régulièrement dans beaucoup de projets.
Code : Sélectionner tout
widget* load_widget( widget::id desired );
Questions :
  • Qu'est-ce qui ne va pas avec le type de retour et que faudrait-il utiliser comme type de retour à la place ?
  • Vous ne souhaitez pas forcément modifier tout votre code existant pour être compatible avec le nouveau type de retour. Comment la compatibilité avec le code ancien sera-t-elle conservée ?


Les solutions proposées doivent évidemment tenir compte de la nouvelle norme. Pour vous aider un peu, cette question est notée avec une difficulté de 5/10 et est dans la série des pointeurs intelligents.

Sans aller voir les réponses proposées sur le site, sauriez-vous répondre à ces questions et être aussi bon que les lecteurs de Herb Sutter ?
Connaissiez-vous les Guru of the Week et les avez-vous déjà lu ?


Vous avez aimé cette actualité ? Alors partagez-la avec vos amis en cliquant sur les boutons ci-dessous :


 Poster une réponse

Avatar de gbdivers gbdivers - Inactif http://www.developpez.com
le 23/04/2012 à 14:19
Citation Envoyé par LittleWhite  Voir le message
Bonjour,

Si la responsabilité n'est pas à nous de détruire le pointeur (cas de la factory, si j'ai suivi), alors il faudrait que le destructeur soit privé (ou protected )

Sauf que widget* n'est pas une variable membre (d'ailleurs, load_widget n'a pas besoin d'être une classe ou une fonction membre), donc pas de notion de public ou private ici.
Une petite révision du design pattern factory ? http://come-david.developpez.com/tut...e=Fabrique#LIV
Avatar de LittleWhite LittleWhite - Responsable 2D/3D/Jeux http://www.developpez.com
le 23/04/2012 à 14:51
Bah, je parlais surtout d'une classe qui gérerai tous les widgets.
Genre, vous charger le widget, le pointeur est gardé dans une "banque" et une copie du pointeur est renvoyé. Mais oui, j'imagine que l'on revient dans le cas de la variable membre ou similaire.
Pour la Factory, je sais mais je ne voulais pas dire un mot interdit
Avatar de Klaim Klaim - Membre expert http://www.developpez.com
le 23/04/2012 à 15:15
Merci gbdivers pour la traduction, j'avais pas le courage.

Pour la solution de JolyLoic je pense qu'avoir un smart pointer qui indique clairement que l'objet est juste référencé (ref_ptr?) mais pas managé par l'utilisateur serait une aide surtout pour totalement suprimer la possibilité d'un delete sur le pointeur (hors .get() qui est facilement trouvable).
Avatar de bountykiler bountykiler - Membre régulier http://www.developpez.com
le 23/04/2012 à 22:47
Bon ben voilà, j'ai répondu à mon tour sur le blog

(En gros, mon idée est de créer une classe owner_ptr, laquelle se chargerai de garder l'ownership de la même manière que ce que fait un unique_ptr, mais qui autoriserait le partage de ce dernier via les weak_ptr.)
Avatar de Klaim Klaim - Membre expert http://www.developpez.com
le 24/04/2012 à 3:46
Je ne vois pas bien la difference avec un shared_ptr. Et aussi ca serait super couteux par rapport a un pointeur qui ne fait qu empecher l utilisateur de faire un delete...
Avatar de Freem Freem - Membre émérite http://www.developpez.com
le 24/04/2012 à 11:03
Je pense que le comité n'a pas été assez loin avec le unique_ptr.
Je sais que weak_ptr n'existe que pour des raisons technique, mais une alternative pour le unique_ptr aurait été intéressante, pour moi.

Pour en revenir a la question, je tiens tout de même a rappeler que d'utiliser des unique_ptr (shared_ptr aussi, bien que plus léger) possède un impact sur le code. Faible, mais existant.

Par exemple, le constructeur par copie est supprimé.
De même, il faut ensuite traquer les delete.
Donc, si on ne veut pas modifier le reste du code, on ne peut pas utiliser les smart_ptr du C++ 11, mais en implémenter d'autres, selon la situation de responsabilité.

Pour la 2nde question:
j'étais déjà tombé sur les GotW, mais j'ai jamais pigé ce que signifiait cet acronyme ^^
En tout cas, c'est une excellente chose que ça reprenne, je pense que je vais suivre ce truc, moi qui cherche a apprendre le C++ en profondeur ça ne peut que m'être bénéfique.
Avatar de gbdivers gbdivers - Inactif http://www.developpez.com
le 24/04/2012 à 11:19
Citation Envoyé par Freem  Voir le message
Pour en revenir a la question, je tiens tout de même a rappeler que d'utiliser des unique_ptr (shared_ptr aussi, bien que plus léger) possède un impact sur le code. Faible, mais existant.

Hum, j'aurais dit l'inverse. shared_ptr possède un compteur de référence, contrairement à unique_ptr et est donc plus "gros" (voir le GotW 102 pour une représentation graphique de la mémoire). Pour moi, unique_ptr n'a pas de surcoût (mémoire ou temps d'accès) par rapport à un pointeur nu.

Citation Envoyé par Freem  Voir le message
Par exemple, le constructeur par copie est supprimé.

Attention, unique_ptr n'interdit pas d'avoir autant de pointeur que l'on veut sur un objet. On peut récupérer des pointeurs nus avec get(). Il dit juste "c'est moi qui ait la responsabilité de détruire l'objet".

Citation Envoyé par Freem  Voir le message
De même, il faut ensuite traquer les delete.

Donc, si on ne veut pas modifier le reste du code, on ne peut pas utiliser les smart_ptr du C++ 11, mais en implémenter d'autres, selon la situation de responsabilité.

Comme indiqué par l'un des intervenants, il sera quand même plus simple de faire une recherche (même sur un gros projet) pour supprimer les delete que de recréer des classes de pointeurs intelligents (sauf peut être le pointeur intelligent proposé par JolyLoic)

Donc, en pratique, on peut modifier un code existante de la façon suivante :
Code : Sélectionner tout
1
2
3
A* a = new A(); 
// utilisation dans tous les sens de a et pleins de copies de a 
delete a; // ou delete une des copies de a ? Le problème est là...
Par
Code : Sélectionner tout
1
2
3
4
unique_ptr<A> a_owner = unique_ptr<A>(new A()); // toujours pas de make_unique... 
A* a = a_owner.get(); 
// utilisation dans tous les sens de a et pleins de copies de a 
// delete a; on supprime les deletes
Avatar de Freem Freem - Membre émérite http://www.developpez.com
le 24/04/2012 à 12:30
Citation Envoyé par gbdivers  Voir le message
Hum, j'aurais dit l'inverse. shared_ptr possède un compteur de référence, contrairement à unique_ptr et est donc plus "gros" (voir le GotW 102 pour une représentation graphique de la mémoire). Pour moi, unique_ptr n'a pas de surcoût (mémoire ou temps d'accès) par rapport à un pointeur nu.

Tu m'as mal compris. Je parlais d'impact sur le code, pas sur la performance.
Shared_ptr possède par exemple un constructeur par copie, et donc peut être passé par valeur, par exemple.
Citation Envoyé par gbdivers  Voir le message
Attention, unique_ptr n'interdit pas d'avoir autant de pointeur que l'on veut sur un objet. On peut récupérer des pointeurs nus avec get(). Il dit juste "c'est moi qui ait la responsabilité de détruire l'objet".

C'est vrai, mais l'utilisation de pointeur nu crée un risque de delete qui rend le pointeur unique un peu fragile, de mon point de vue. Ce qui ne m'empêche pas d'utiliser unique_ptr quand je le peux.
Citation Envoyé par gbdivers  Voir le message
Comme indiqué par l'un des intervenants, il sera quand même plus simple de faire une recherche (même sur un gros projet) pour supprimer les delete que de recréer des classes de pointeurs intelligents (sauf peut être le pointeur intelligent proposé par JolyLoic)

Donc, en pratique, on peut modifier un code existante de la façon suivante :
Code : Sélectionner tout
1
2
3
A* a = new A(); 
// utilisation dans tous les sens de a et pleins de copies de a 
delete a; // ou delete une des copies de a ? Le problème est là...
Par
Code : Sélectionner tout
1
2
3
4
unique_ptr<A> a_owner = unique_ptr<A>(new A()); // toujours pas de make_unique... 
A* a = a_owner.get(); 
// utilisation dans tous les sens de a et pleins de copies de a 
// delete a; on supprime les deletes

Ce fameux make_unique serait effectivement intéressant.

Dans le code remplacement, il reste que si il y a eu transfert de responsabilité par copie dans une branche du code mais pas toutes, on s'expose à des delete multiples sur une même adresse mémoire.
Bien sûr, on devrait remettre un pointeur a nul quand on transfère sa responsabilité a quelqu'un d'autre, mais ce qui devrait être fait ne l'est pas toujours.
Par exemple:
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
 
A *a=new A(); 
if(cnd==true) 
{ 
  A *b=a; 
  myFunc(b); 
} 
else 
  delete a; 
return;
Bien sûr, ce code n'est pas propre, a aurait dû être remis à 0 lors du transfert. Bien que d'un point de vue optimisation, avant C++11 et la move semantic, ça aurait été un gâchis de ressource processeur selon mes faibles connaissances (il y a peut-être une alternative a std::swap que je ne connaît pas).
Mais on a pas la garantie qu'il n'existe pas, dans un code qui n'est pas de soi-même. Si on fait un simple "trouver/remplacer" par exemple, on s'expose à un crash, car unique_ptr nettoiera la mémoire qui aura été nettoyée par myFunc.
Donc, remplacer un pointeur ancien par un unique_ptr est une opération à ne pas prendre à la légère pour moi, et je ne m'y risquerais pas sans contrôler tous les usages du code que j'ai altéré.
Avatar de bountykiler bountykiler - Membre régulier http://www.developpez.com
le 24/04/2012 à 13:06
Citation Envoyé par Klaim  Voir le message
Je ne vois pas bien la difference avec un shared_ptr. Et aussi ca serait super couteux par rapport a un pointeur qui ne fait qu empecher l utilisateur de faire un delete...

Par rapport à un shared_ptr, on n'a pas de compteur pour les réferences fortes. De même, un owner_ptr n'aurait pas de constructeur de copie (juste un move constructeur)
Sinon, au niveau du cout ce serait effectivement plus couteux qu'un simple unique_ptr, mais plus léger que des shared_ptr.
Avatar de xbretzel xbretzel - Membre à l'essai http://www.developpez.com
le 25/04/2012 à 14:55
Pour ma part, je n'ai jamais embarqué aussi profondément dans ces aspects sombres et techniques ( alien :-) ) du C++ ! Je suis un vieux codeux en C, qui adore le C++ mais piégé dans l'ancien mode permissif potentiellement chaotique du C. C'est tout juste si je comprend la surface des templates template <typename T> class A{....};. Alors 'a' dans A* a = new A(...);. Je sais que je suis responsable de la durée de vie de 'a' - un point c'est tout...
C'est qui le programmeur maintenant ? le langage ? ou le codeux ?
Avatar de Flob90 Flob90 - Membre expert http://www.developpez.com
le 25/04/2012 à 15:27
@xbretzel: Et quand tu fais
Code : Sélectionner tout
1
2
 
int a;
Ce n'est pas le langage qui s'occupe de a ? Meme en C ? D'ailleurs dans
Code : Sélectionner tout
1
2
 
int* a = new int;
Tu n'es pas responsable de a au sens propre, mais de l'objet pointé par a, cad *a.

Quand on parle de responabilité de la durée de vie, la question n'est pas de savoir si c'est le langage ou le programmeur qui s'occupe de ca, mais surtout où le faire ?

Ensuite en C++ on utilise des capsules RAII qui transfert ca au langage, c'est nécessaire en particulier à cause des exceptions. En C il n'y a pas d'excpetion, cependant la gestion propre des erreurs et des allocations dynamique n'en est pas plus simple (goto pour faire du RAII entre autre).

Ensuite vient une question de "verbosité" que se pose Loic Joly. Le langage nous permet d'écrire différentes capsule (*) qui indique clairement si l'objet doit être détruit par l'appelant, si la fonction qui l'a crée s'occupe déjà de le détruire (**) ou enfin si ils ont besoin de pouvoir le détruire les deux. Ainsi Loic remarque qu'il manque une capsule pour dire : "la fonction s'occupe déjà de le détruire" (***).

(*) Si il existe assez de capsule, alors le besoin de pointeur nue dans le code métier peut totalement disparaitre. De même que le besoin de new/delete (toujours dans le code métier) avec les fonctions de création (make_shared,make_unique).

(**) Dans le cas où il peut ne pas y avoir d'objet retourné, sinon une référence me semble plus indiqué. D'ailleurs je pense qu'en terme d'utilisation, std::unique_ptr et les références doivent passer devant std::shared_ptr et une éventuelle nouvelle capsule.

(***) Dans un monde idéal sans se préoccuper "d'ancien code" ca ne serait pas nécessaire : pointeur nue = pas de responsabilité.
Offres d'emploi IT
Architecte fonctionnel et applicatif (H/F)
Société Générale - Ile de France - Val-de-Marne
Chargé(e) de mission au CERT Société Générale (H/F)
Société Générale - Ile de France - Val-de-Marne
Ingénieur développeur intégrateur débutant H/F
Safran - Ile de France - Osny (95520)

Voir plus d'offres Voir la carte des offres IT
Contacter le responsable de la rubrique Accueil