pour la guerre de religion reference/pointeur j'ai dit plus haut que:
* pointeur c'est plutot pour les entités
* référence c'est plutot pour les valeurs
je n'ai vu personne commenter mais je me demande quand même ce que vous en pensez.
pour la guerre de religion reference/pointeur j'ai dit plus haut que:
* pointeur c'est plutot pour les entités
* référence c'est plutot pour les valeurs
je n'ai vu personne commenter mais je me demande quand même ce que vous en pensez.
En y réfléchissant, ce que tu dit pourrais être pratique pour la compréhension du code.
Mais j'ai l'impression que cela reviens à dire qu'une entité est forcement alloué dynamiquement. Ce qui me gêne un peu.
Perso, j'ai l'impression que les références sont mieux comprise que les pointeurs. Ou plutôt mieux utilisé. Je trouve que dès qu'il y as un pointeur y as 10000 cas possible pour qu'il soit invalide. Alors qu'un référence y en as 100.
[edit]
Remarque en Qt
les QObject (classes entités) sont utilisés sous forme de pointeur
les classes valeurs (QString, QVector,...) sont utilisés en copy et const référence. Ou en pointeur quand c'est une sortie.
Disons que dans la plupart des cas cette définition me convient.
J'essaye de me tenir a ce qui suit:
* une valeur est definit avec le mot clé struct, et ne contient pas d'entité (mais eventellement un pointeur sur une entité), donc n'est qu'une aggrégation de valeurs
* une valeur devrait redéfinir les opérateurs ==, = ainsi qu'un constructeur de copie et (souvent) un operateur <. En tous cas, ceux-ci ont un sens même s'ils n'existent pas.
* les valeurs sont passés par copie ou par référence pour éviter la copie
* une entité est définie avec le mot-clé class
* une entité pourrait contenir une autre entité, bien que dans la plupart des cas il s'agit de pointeurs sur des entités
* une entité peut contenir des valeurs
* les opérateurs == ou < n'ont pas vraiment de sens
* les entités sont manipulées en general via des pointeurs
En prenant ces définitions j'ai du mal a voir dans quel cas (non tordus) une entité peut ne pas être allouée dynamiquement. Disons que c'est plus rare d'utiliser une entité "localement", c'est plutot le travail d'une valeur
Dans le cas plus haut, ce qui me choque dans l'exemple de dédé c'est ce code:
ou MyObject est une entité.
Code : Sélectionner tout - Visualiser dans une fenêtre à part void DoSomething(const MyObject& o) { o.SpeakUp(); ... }
Ce code a peu de sens, et j'ai cherché a savoir ce qui me chiffone. Le passage d'une référence constante sur cet objet n'a pas vraiment de sens...
Or, MyObject est une entité (l'opérateur d'affectation n'a pas vraiment de sens, et il est impossible de définir logiquement un opérateur ==, et pas de copie possible). En effet, MyObject est juste une aggrégation de comportements.
Ce qui semble naturel pour moi c'est d'ecrire
la je vois parfaitement ce que ca veut dire et l'utilisation d'un pointeur (bien qu'il soit nu, et ca je ne cautionne pas forcément) résout le problème de l'auteur.
Code : Sélectionner tout - Visualiser dans une fenêtre à part void DoSomething(MyObject* o) { o->SpeakUp(); ... }
Et ca ne me choque pas que toute les entités d'un programme soient allouées dynamiquement, ce qui me choque c'est l'allocation dynamique de valeurs
Pour le 1-, il y a un problème je suis d'accord, mais il est bien plus profond que const comme Joel l'a signalé.
Pour le 2-, je partage l'avis de gbdivers. C'est déjà bien d'avoir une garantie entre moi et moi-même (mais aussi les autres dév). Si en plus le compilo vérifie les contrats pour nous, c'est magique! À moi ensuite de ne pas chercher à contourner les invariants et autres contraintes d'utilisation que je pense avoir identifiés.
Et si je vois en cours de route je vois que j'ai eu la main trop lourde, je passe du cas restreint (à hypothèses simplificatrices) au cas général.
Accessoirement, tu viens de nous dire que les types ne servent à rien car il y aura toujours un couillon pour les réinterpréter des int en double.
Pour le 3-, J'ai déjà re-constifier des codes qui ne l'étaient pas. Souvent, il s'agissait de codes qui ne m'appartenaient pas, vu que je mets les const dès le début.
Du temps perdu ? Oui et non, ce fut aussi l'occasion de rentrer dans un code développé par un autre.
EDIT: zut, j'ai validé le message trop tôt.
Concernant la maintenabilité, dans "char* Func(int*, float*);" je lis que les deux buffers en entrée vont être altérés (ben oui, ce ne sont pas des références qui sont prises, donc c'est tableau C en in/out). Or je ne veux pas que la fonction que j'appelle les modifient. Que fais-je ? Ben je leur passe une copie. Et je perds du temps au passage à aller fouiller code & doc pour m'assurer que je ne perds pas la responsabilité de mes tableaux.
Alors que "func(std::auto_ptr<int>, float& r, vector<double> cont& )" me dit que la fonction va assumer la responsabilité d'un pointeur sur un (seul) int, modifier un flottant (éventuellement lire sa valeur), et utiliser sans altérer un vecteur qui est reçu en paramètre.
Côté maintenance, c'est clair comme de l'eau de roche, nul besoin d'aller ouvrir la doc ou le code pour savoir ce que je dois dupliquer, ce que je peux libérer, etc.
Il arrive qu'en paramètre je puisse prendre un "T const*", mais malgré les ambiguïtés sémantiques possibles, je considère par défaut qu'il s'agit d'un tableaux d'éléments que je n'ai pas le droit de modifier, probablement des histoires trainantes d'interfaçage avec du C.
Il y a-t-il vraiment besoin d'écrirePour les références, j'ai suffisamment argumenté et illustré qu'elles n'apportent rien de plus que les pointeurs si ce n'est une certaine légèreté syntaxique. Je comprends l'argument que les références apportent une sécurité accrue, mais pouvez-vous illustrer vos propos?
pour comprendre pourquoi on prend systématiquement des références quand on a le choix ?
Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 void f(T * p) { assert(p && "erreur de prog: je ne sais pas travailler avec un pointeur nul, mon dev est fainéant et refuse de perdre du temps sur les cas généraux qui ne riment à rien"); } void g(T & v) { ... }
b- J'ai expérience de deux situations de ce genre :
- j'ai trop joué avec des templates et sans équipement pour en simplifier les messages d'erreur (static_assert, et autre logger à templates)
- j'ai regardé un code d'un autre disposant du genre d'erreur que je ne commet plus (inversion entre , et ; etc)
Et mes problèmes n'ont jamais été induits par des références.
------------
Une référence et/ou un const prouvent plus qu'un TU vu qu'il faut le faire exprès pour les contourner. Ils sont vérifiés à la compilation, et ils m'évitent de faire un test qui valide que ma fonction me modifie pas le paramètre que je lui passe (cas du const), ou une batterie d'autres tests qui vérifient que ma fonction sera toujours appelée avec un pointeur non nul.
Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...
Prends ta fonction g, remplace la référence par un pointeur, recompile, et il n'y a pas magiquement plus de chances que v soit nul. On peut très bien s'assurer qu'un pointeur est non nul par design, sans besoin d'assertions ou autres. Il suffit de brièvement consulter n'importe quel code Java, C#, Python, Ruby, VB, etc., pour se rendre compte que dans ces langages où tout est une référence potentiellement nulle, on ne passe pas davantage son temps à faire des assertions ou à vérifier les valeurs de retour. (La gestion de mémoire automatique ne règle que le problème des références invalides, pas des références nulles.) Et qui plus est, on n'a pas deux syntaxes pour représenter la même chose, et on ne se pose jamais la question de laquelle faudrait-il choisir.Il y a-t-il vraiment besoin d'écrire
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 void f(T * p) { assert(p && "erreur de prog: je ne sais pas travailler avec un pointeur nul, mon dev est fainéant et refuse de perdre du temps sur les cas généraux qui ne riment à rien"); } void g(T & v) { ... }
De toute manière, le vrai problème ce sont les pointeurs et références invalides ("dangling"); la cause d'un pointeur nul est relativement simple à identifier, celle d'un pointeur ou d'une référence invalide, moins. Et à ce niveau le fait d'utiliser une référence n'offre aucune garantie particulière.
Je ne nie pas le fait qu'il est parfois pratique d'utiliser une référence, comme dans ton exemple. Idem pour const, j'y reviendrai. Mais de façon générale, C++ est un langage inondé de fonctionnalités qui ne sont applicables que dans une minorité de cas, et quoiqu'elles puissent être utiles dans ces cas, le coût en complexité n'en vaut pas la peine d'après moi et mon expérience avec des langages plus simples. Je préfère un C++ quelque peu réduit et c'est comme ça que moi et mon équipe sommes les plus productifs. As-tu déjà essayé de faire un projet de taille moyenne sans le moindre const? Quand tu n'as même plus à te poser la question, c'est tout cette énergie que tu peux investir sur "résoudre le problème" plutôt que "se conformer au langage".
Concernant const, je vais essayer d'être parfaitement clair. C++, comme tous les langages impératifs, est un langage où tout est mutable par défaut. Le principe d'un langage impératif est en effet de modifier l'état du programme de façon séquentielle. Or les langages impératifs offrent souvent une façon de garantir qu'un membre de donnée ne peut être accidentellement réassigné ("final" en Java, "readonly" en C#, etc.) En C++, const va beaucoup plus loin. Étant donné que const se propage à tout ce qu'il touche, on se retrouve avec un langage où tout doit être const par défaut, explicitement, et ce qui ne l'est pas doit même parfois être déclaré mutable. Comme tout est mutable par défaut en C++, il en revient au programmeur de renverser la vapeur en saupoudrant des const partout où il est possible d'en mettre. Et ainsi, const devient omniprésent. Chaque méthode non-const a son équivalent const, et on n'en finit plus de dupliquer les signatures. La question d'immutabilité se pose au plus fin niveau de granularité (chaque méthode, chaque paramètre, chaque niveau d'indirection d'un pointeur même), à chaque instant, alors qu'il est rare que l'immutabilité soit si importante. Quel temps perdu!
En d'autres termes, si const pouvait être utilisé au besoin, je ne dirais pas non. Mais étant donné que c'est tout ou rien, je préfère rien. C'est clairement une fonctionnalité intrusive et qui interagit mal avec les templates comme j'ai illustré, donc mal pensée. C'est la faute de la spec des templates, oui, reste que ça brise const.
Tu dis que tu as déjà eu à rectifier du code qui n'était pas const. Mais que fais-tu si tu ne peux pas le modifier? Si TypeA n'offre pas de méthode const, et que je ne peux modifier le code source de TypeA, eh bien zut je ne peux pas m'en servir dans les méthodes const de mon type. Que fais-je? Je créé des équivalents non-const. Et ainsi je me retrouve avec des signatures dupliquées (si ce n'est des implémentations dupliquées, dans le cas de templates). Maintenance : -1.
Et oui, const sur un paramètre m'informe, sans avoir à lire la documentation, que tel paramètre ne sera pas modifié. Il m'informe aussi que si la fonction doit utiliser une certaine méthode de mon type, cette méthode devra être offerte en version const, ce que je ne peux savoir qu'en regardant son code source ou en tentant de compiler. Une simple description, qu'un bon IDE devrait faire apparaître sans même un clic, aurait eu le même effet sans imposer de contraintes sur mon code. Maintenance : -1.
De plus, tu supposes qu'un paramètre pris par référence non-const sera modifié. Or, c'est ambigu en fait. Peut-être est-ce un simple oubli de la part de celui qui a écrit la fonction. Il vaudrait mieux lire la description de la fonction que d'essayer de deviner l'intention de son auteur. Maintenance : -1.
Les types sont effectivement une chimère en C++ puisque ce avec quoi on interagit réellement, c'est de la mémoire brute. Or, reste que les types sont toujours utiles et toujours applicables. Ils ne garantissent rien, mais ils permettent au moins de diviser logiquement le programme, ce dont on aurait de la difficulté à se passer. Donc, chimère oui, mais chimère utile. const, quant à lui, ne garantit rien et ne fait que se répandre comme la peste.Accessoirement, tu viens de nous dire que les types ne servent à rien car il y aura toujours un couillon pour les réinterpréter des int en double.
Il est vrai que le c++ à un problème dès qu'on touche aux template :
La plupart des mots clés (const notamment) obligent de créer une autre version de la même fonction. Pour cela, il suffit de lire le header functional du standard (si on regarde celui de c++0x, on obtient en plus les && pour dire rvalue).
Horrible tous ces templates alors que le code est le même !
Le const est très contreproductif. Cependant, il y a obligation de le mettre (ou mettre des const_cast partout).
Cependant, const a de nombreux avantages : il prévient la mauvaise utilisation : Combien de fois aurions nous vus "J'ai un problème avec std::string car il prend une mauvaise valeur après avoir appelé c_str() et avoir assigné la valeur".
Toutefois, je pense que cette discussion ne devrais pas avoir lieu dans "Pointeur intelligent vs pointeurs bruts" (Une scission de la discussion ?)
[EDIT]
J'aurais tendance à être d'accord avec Dr Dédé quand il dit que les références ne permettent que des raccourcis syntaxiques : toute référence est remplaçable par un pointeur non null.
La référence à le même défaut que le pointeur : elle peut être invalide.
Si une fonction prend un pointeur, il suffit de dire en précondition : "le pointeur ne peut être null" et, à partir de ce point, soit on considère qu'il faut quand même faire un assert (pour ceux n'ayant pas lu) ou alors considérer qu'il n'est pas null.
Mais la aussi, c'est une autre discussion : il ne s'agit pas de pointeurs intelligents et de pointeurs bruts...
Pour en revenir au sujet, les pointeurs intelligents sont utiles car ils permettent d'éviter la fuite mémoire, même en cas d'exception (ce qui j'aurais tendance à dire que ce n'est pas grave puisque une exception est sensée être extrêmement rare...).
@NoIdea: A part qu'un pointeur peut-etre NULL alors qu'une référence non. Et si en effet une référence peut-etre invalide au même titre qu'un pointeur, ces cas sont souvent des erreur de programmation (comprendre qui n'existe pas si le programme est bien fait), alors qu'un pointeur NULL n'est en aucun cas un problème de programmation et à un vraie sens, et doit donc être traité. (sauf si le contrfat dit l'inverse, mais dans ce cas pourquoi prendre un pointeur ? Ca sera juste plus verbeux à écrire et difficile à lire.)
@yan: Je ne comprend pas ton étonnement suite à l'édite de NoIdea, l'existence des deux versions pour chaque constante est primordiale, donc ce n'est pas inutile, et c'est bien une duplication de code (qu'on peut de temps en temps éviter, en jouant avec des cast par exemple, cf Eff++ item sur l'opérateur d'affectation je crois).
@Dr Dédé:
Tout les exemple que tu cites pour les const et les types sont vraie, mais ils supposent quand même un code de départ en désaccord avec ce qu'on dit. (même si ils fonctionnent )
Tu dis que tout est mutables par defaut, et je veus bien te croire, mais si j'ai un objet (d'un type quelconque), et que je lui envoyes le message "afficher" il est clair que je ne m'attends pas à ce que l'objet soit modifié, c'est en partie ceci que garantie const : que l'utilisateur ne modifie pas d'objets à son insue (sa suppose que le code soit totalement const-correct).
Pour tes exemple 1 et 3 (code de départ non const-correct et l'oublie), mélanger deux facon de coder conduit rarement à de bon résultat, et pour l'oublie : quand on fait une erreur c'est rare que ca marche bien ...
Le C++ est quand même typé, on peut en effet réinterpréter les données, mais c'est aux risques et périls de celui qui le fait, donc à moins de "se tirer une balle dans le pied" (copyright koala) je ne crois pas que ce soit un réel problème. (Ou alors le problème est que le C++ permet ce genre de chose, mais c'est une tout autre discussion AMA).
J'ai déplacé les messages sur le mot clef const sur
http://www.developpez.net/forums/d99...ts-clef-const/
yan
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager