Cas où les références remplacent mal les pointeurs ? - C++ - Programmation
Marsh Posté le 01-10-2009 à 11:14:34
je vois pas le pb ... donne splsu de details sur les type etc la je comprends rien.
Marsh Posté le 01-10-2009 à 11:27:13
La déclaration de classe (bidon) ca peut aider ?
Code :
|
En fait, mon raisonnement c'est plus de savoir où mettre la limite entre l'usage des références et l'usage des pointeurs !
J'ai la conviction que 99% des problèmes n'ont besoin que de références et qu'un cas très réduit de traitement ne peuvent s'en passer (polymorphisme...)
Car dans l'idéal, je voudrais utiliser des références partout où c'est possible (quitte à en chier).
Marsh Posté le 01-10-2009 à 11:31:06
File c'ets quoi ?
Sinon t'as aussi du polymorphisme sur les references hein :€
Marsh Posté le 01-10-2009 à 12:03:54
Je peux me tromper, mais selon moi ça donne ça :
Une référence ne peut pas être nulle, donc ton membre de classe m_pFile qui est nul, puis non nul une fois passé dans ta méthode de loading (ou ton ctor si tu es plus dans le style RAII), puis re nul une fois passé dans ta méthode d'unloading (ou ton destructor si RAII) ça doit être un pointeur je pense.
Par contre faut utiliser les références dès que tu veux passer un objet à une fonction (et pas une copie), plutôt que de prendre un pointeur sur l'objet (et de passer une copie du pointeur du coup, C style).
Enfin t'as les auto_ptr aussi, cf. l'exemple de la faq de B. Stroustrup :
Code :
|
Je dis ptet de la merde hein, faut attendre confirmation de Joel F
edit : ton exemple rappelle un peu http://www.research.att.com/~bs/bs_faq2.html#finally
Marsh Posté le 01-10-2009 à 18:05:27
L'approche RAII a l'air d'etre beaucoup plus adapté à une approche "throw d'exceptions".
Tandis que l'approche methode loading est bien aussi, mais plus étape par étape, et moins diagramme de classe... je peux me tromper...
unique_ptr est mieux que auto_ptr ? et boost::shared_ptr ??
Marsh Posté le 01-10-2009 à 21:59:12
un peu HS, mais
Code :
|
return dans un constructeur ? Après un throw ?
C'est peut-être toléré dans un certain compilateur, mais ne me semble pas très très canonique tout ça.
Sinon dans ce cas un smart-pointer serait très bien, voir boost::xxx_ptr ou std::auto_ptr si on veut rester STL-98, ça a déjà été dit.
Marsh Posté le 12-10-2009 à 16:21:32
NounouRs a écrit :
|
Comme une référence, c'est un raccourci, tu dois d'abord faire exister l'instance avant celui-ci. Donc tu n'a pas le droit de le créer en même temps que l'objet qui possède ce raccourci. Il faut le faire avant, et passer à ton constructeur le File, déjà crée, voulu.
Le c++ n'est pas du java. Une référence en C++, c'est un vieux lazy tweak. Car tous ce que tu peux faire avec des références, tu peux le faire avec des pointeurs (techniquement), mais pas l'inverse.
Maintenant, il faut te poser des questions de conception. N'ayant pas tous les éléments, je répondrait de façon générique :
Dans se cas, je n'utiliserai ni référence, ni pointeur, mais juste une valeur. Ca vaut pas le coup d'économiser les rares copies que tu vas en faire; vu qu'en plus, c'est pas très clean de faire des copies d'instance partageant le même fichier.
Marsh Posté le 12-10-2009 à 21:49:00
Lavock a écrit : |
les liste d'initialsiations font ça très bien hein ...
Lavock a écrit : |
La seule vrai différence est qu'une ref. n'est jamais nulle, c'est à peu prés tout
Lavock a écrit : |
Surtout que le vrai coup du File c'ets les ressources systèmes, pas tant la mémoire
Marsh Posté le 13-10-2009 à 09:20:12
Dans son exemple, on peut au final très bien utiliser une référence tout en vérifiant le nom du fichier:
Code :
|
Après suivant ce qu'est File, soit la fonction de vérification renvoie un File soit juste la string vérifiée. J'aime bien aussi utiliser les références à la place des pointeurs
Marsh Posté le 13-10-2009 à 09:51:04
Joel F a écrit : |
Oui, mais on ne peut appeler qu'un constructeur par recopie d'un objet non temporaire...
Polo37 a écrit : Dans son exemple, on peut au final très bien utiliser une référence tout en vérifiant le nom du fichier:
|
D'où le fait que pour ce code, tu devrais obtenir un joli "invalid initialization of non-const reference" non ?
La seul solution serait de faire brutasse un :
Code :
|
ou, si tu tiens vraiment à la vérif :
Code :
|
Par contre, ça entraîne un destructeur non trivial, des vérifications en cas de copie, ainsi que plein de chose en cas de RAII ou autre.
Joel F a écrit : |
Exact, du coup, ça empêche pas mal de flexibilité quand même ! A cela, il faut ajouter le fait que ça s'utilise comme une variable classique (oui, ma définition de lazy tweak et parfois moindre ).
Marsh Posté le 13-10-2009 à 09:59:44
ouais j'avoue avoir bien loosé sur ma réponse...
Effectivement l'utilisation d'une référence sur le File impose
soit que la durée de vie du file est gérée par le FileLoader (auquel cas, pas besoin de référence, autant utiliser directement un File en attribut privé)
soit que la durée de vie du file est gérée par autre chose et donc il faut passer au constructeur du FileLoader cet autre chose (ce qui revient en fait à ce que tu disais Lavock)
Après sur le coup de la flexibilité je suis d'accord mais généralement la contrepartie que ça t'apporte c'est la sécurité (tu travailles toujours avec des instances valide et pas à moitié initialisées)
Marsh Posté le 13-10-2009 à 10:13:40
Oui, je case aussi la sécurité dans la partie lazy tweak. Après, je dis pas qu'il sont inutile, j'en utilise tout plein. Et puis effectivement, ça fait pas de mal de ne pas pensez à la sécurité des pointeurs (généralement, on a d'autre chat à fouetter).
En tout cas, il semble qu'on soit d'accord sur le problème. Et vu que tu (Nounours) a apparemment pas envie de gérer de la mémoire, la meilleur alternative c'est un File ni* ni &...
Marsh Posté le 13-10-2009 à 11:13:31
Joel F a écrit : |
Surtout une ref n'est pas rebindable. Et naturellement, il y a la raison pour laquelle
les references ont ete introduite: on peut surcharger les operateurs avec des refs,
pas avec des pointeurs.
Marsh Posté le 13-10-2009 à 11:42:18
Un Programmeur a écrit : |
erf, je pensais que dans l'absolue on pouvait sortir du code quiche-style :
Code :
|
Mais bon, même si c'était faisable, bonjour la lisibilité...
Marsh Posté le 13-10-2009 à 12:04:22
Il faut au moins un parametre qui soit une classe, une reference vers une classe, une enumeration ou une reference vers une enumeration.
Marsh Posté le 13-10-2009 à 15:38:32
Lavock a écrit : |
En C++0x, je pense que tu fais un std::move.
En C++03 par contre oui
Marsh Posté le 13-10-2009 à 16:40:46
Lavock a écrit : |
Tu fais allusion au fait qu'on ne peut pas binder un temporaire a une reference non constante ou bien s'agit-il d'autre chose?
Marsh Posté le 13-10-2009 à 17:49:03
Ca plus le fait qu'on ne peut pas appeler un constructeur non-recopiant.
Joel F a écrit : |
Tient, d'ailleurs, on ferait ça comment ?
Code :
|
Il me sort un "invalid initialization of non-const reference"...
Marsh Posté le 13-10-2009 à 18:09:44
Il faut que m_file est un constrcteur avec rvalue-ref
Marsh Posté le 13-10-2009 à 21:01:52
En gros, ça en revient à ça :
Code :
|
Sauf que du coup, la durée de vie sera gérée par le FileLoader ?
Marsh Posté le 13-10-2009 à 21:21:37
le new est inutile, renvoit un object creer sur la pile.
Marsh Posté le 14-10-2009 à 09:32:18
Pire, avec ce code le delete n'est jamais fait.
Marsh Posté le 14-10-2009 à 16:56:46
Bon, pour recentrer, et pour résumer, après avoir fais joue-joue avec les rval, lval, et autres mov (NounouRs, ta participation est sollicitée):
Conseil -> n'utilise n'y pointeur, ni ref.
Si tu veux vraiment utiliser une ref ( l/rvalue ref en c++0x) --> tu dois faire comme je l'ai précisé plus haut.
Explication : le move constructor est inefficace : on ne peux pas initialiser une lref avec ce constructeur. Même avec la présence d'un std::move ;qui, rappelons le, est juste chargé d'appeler le move constructor plutôt que le constructeur s'il est dans un constructeur.
Quoi qu'il advienne, dans un même bloc, une pauvre rvalue n'est destinée qu'à rester rvalue * !
Si jamais, malheur t'en prend, tu fais comme dans l'exemple suivant :
Code :
|
Sache que les appels s'enchaînent comme ceci : Constructeur de A, Destructeur de A, Constructeur de B. (Au passage, oui un move constructor et un move on était défini pour A). Il n'y a pas de présence du move constructor à l'endroit voulu (juste avant le destructeur). Du coup, gare aux erreurs !
La seule question qui reste suspendue à nos méninges; c'est pourquoi, oui mais pourquoi, ne peut t'on appeler un move constructor sur une ref(l ou r), et ce, même en la forçant (exemple ci-dessus) ?
La réponse, bien que simple, n'est pas forcément instinctive. Juste que lors de l'initialisation d'une ref, aucun constructeur n'est appelé. D'où l'inutilité de l'opérateur move.
Du coup, je pense que le "discours" tenu entre M. l'illustre (sincèrement) Joel F. et moi-même n'était que quiproquo... Ou alors, j'ai rien compris !
Note : * Pour ceux qui ne savent pas, on peut initialiser une lvalue ref qu'avec une lvalue. Donc il possible d'initialiser une lvalue ref avec une rvalue ref (qui est une lvalue). Malheureusement, l'utilisation d'une rvalue ref ne permet en rien l'extention de vie d'une variable -> Une fonction renvoyant une rvalue ref n'as pas vraiment d'utilité, et cette valeur sera d'ailleurs une rvalue (donc impossible de l'attaché à une lvalue ref).
P.S : Une fonction Type&& retournant une rvalue temporaire indique un warning. Il est possible de fourbé le compilateur en mettant un
Code :
|
mais attention au bug du runtime !
P.P.S : Cas intéressant, changeons le code ci-dessus par :
Code :
|
Marche parfaitement bien ! [Edit] En revanche, cela prvoque une fuite mémoire qu'il ne sera pas évident de colmater !
[Edit] Cf. Pensée saugrenue quelques postes plus bas !
Marsh Posté le 14-10-2009 à 17:25:12
Lavock a écrit : |
<mode enculage de mouche mais jsuis gros noob dans ce domaine>
ya pas une histoire de rvalue ref nommée qui est une lvalue et de rvalue ref pas nommée qui est une rvalue ?
Marsh Posté le 14-10-2009 à 17:34:43
Oui, d'où
Lavock a écrit : Malheureusement, l'utilisation d'une rvalue ref ne permet en rien l'extention de vie d'une variable -> Une fonction renvoyant une rvalue ref n'as pas vraiment d'utilité, et cette valeur sera d'ailleurs une rvalue (donc impossible de l'attaché à une lvalue ref). |
[Edit] Et un grand merci à toi pour cette explication simple mais efficace qui m'échappait.
Marsh Posté le 14-10-2009 à 17:50:14
Lavock a écrit : Bon, pour recentrer, et pour résumer, après avoir fais joue-joue avec les rval, lval, et autres mov (NounouRs, ta participation est sollicitée): |
Ouais on a du se melanger les pedales là. Lire dans l'ordre chronologique :
http://cpp-next.com/archive/tag/value/
Marsh Posté le 14-10-2009 à 20:35:04
Il y a de la confusion. Ou de ma part, ou de la votre. Je n'ai malheureusement pas le temps de vérifier en détail. Mais quand je lis
Lavock a écrit : Cas intéressant, changeons le code ci-dessus par :
|
je me demande où on libère la mémoire allouée par le new. (A mon avis, jamais, ce qui est une fuite de mémoire chez moi).
Marsh Posté le 14-10-2009 à 20:36:16
ce code est faux pour la simple raison qu'il leak.
Pour Lavock, il fonctionne car il ne plante pas.
Marsh Posté le 14-10-2009 à 22:38:31
Un Programmeur a écrit : Il y a de la confusion. Ou de ma part, ou de la votre. Je n'ai malheureusement pas le temps de vérifier en détail. Mais quand je lis je me demande où on libère la mémoire allouée par le new. (A mon avis, jamais, ce qui est une fuite de mémoire chez moi). |
Au temps pour moi ! Ca leak (et non M Joel, je sais ce qu'un programme ); et vraiment aucun moyen de libérer ce fichu pointeur...
Joel F a écrit : En C++0x, je pense que tu fais un std::move. |
Du coup, non, C++0x ou pas, ça change rien... une rvalue restera rvalue; sauf pour ce code alambiquer qui n'en veut pas la peine :
Code :
|
(D'ailleurs, le résultat et le même si on enlève un &, ainsi que le *&....)
En tout cas, ça m'aura appris plein de chose sur ce nouveau standard, même si je ne suis pas l'auteur du sujet, et que c'était même pas le sujet...
[Edit] Pensée saugrenue : on pourrait, pour colmater la brèche, adopter une convention --> le A(const A&& ) agierait normalement, mais le A(A&& ) ferait un delete de la rref passez en paramètre... Un moyen d'exploiter la const surcharge ! Mais ça oblige pas mal de formalisme.
Marsh Posté le 15-10-2009 à 06:56:54
le truc c'est que tu renvois ce *new A
Je pense que si tu renvoit un A(), tu as un non-named rvalue et tout ce passe bien.
Marsh Posté le 15-10-2009 à 08:24:20
Joel F a écrit : le truc c'est que tu renvois ce *new A |
Oui, si je cherche à init un A; pas de problème. Mais je pouvais aussi le faire en c++03; cela aurait toutefois appelé le copy ctor.
Si je cherche à init une ref; non.
Lavock a écrit :
mais attention au bug du runtime ! |
Même en fourbant le compileur, comme aucun constructeur n'est appelé sur une ref, la variable est détruite. Du coup, c'est toujours accessible; mais la valeur stocké est fausse...
Marsh Posté le 15-10-2009 à 08:26:01
ce que je voulais dire c'es que vu la semantique de la rvalue copy, t'as plus besoin d'avoir une ref., tu vas stocker un insance et la move-copiée
Marsh Posté le 15-10-2009 à 08:31:46
Sur ça, on est d'accord; comme je le disais depuis le début, le mieux c'est de ne pas utiliser de ref, ni de pointeur.
Mais, comme c'était la question de nounours, je cherchais, à, dans se cas, utiliser une ref....
Marsh Posté le 15-10-2009 à 08:36:43
oui mais non a cause de la semantique lvalue/rvalue/named rvalue.
Marsh Posté le 15-10-2009 à 10:22:55
Pour revenir au probleme initial,
NounouRs a écrit :
|
Code :
|
et pas besoin de references ni de pointeurs.
Marsh Posté le 16-10-2009 à 23:35:15
ça donne quoi une exception dans le constructeur ? faut try-catcher la construction de l'objet ? c'est ok comme conception ? pas de problème de fuite de mémoire ?
Marsh Posté le 17-10-2009 à 09:27:15
Les membres déjà construits sont détruits et l'exception est propagée.
Quelque chose qui surprend certains, on peut faire
Code :
|
pour traiter les exceptions jetées pendant la construction des membres et des classes de base. Mais c'est d'une utilité rare (je ne me souviens pas avoir utilisé cette construction dans du code en production).
Marsh Posté le 01-10-2009 à 11:09:59
Bonjour,
Non, ce n'est pas un troll sur les pointeurs et les références. Je vais rester terre à terre et poser juste une question.
J'essaye à chaque fois d'utiliser des références là où à première vue, j'aurai mis un pointeur (mauvais réflexe). Et voici un cas où mes compétences touchent à leurs limites.
Peut importe le contexte réel, il y a de nombreux cas similaires où le pb. peut se poser... (chargement de module, de fichier, de socket... tout objet sans constructeur vide)
Voici une classe FileLoader (bidon) qui avant de lancer le chargement concret d'un fichier, vérifie la chaine de caractères qui lui est passée comme nom de fichier.
Si je veux remplacer ma variable m_pFile par une reference (au lieux d'un pointeur), je ne peux plus faire ma vérification sur le nom de fichier... problème...
Alors je vois 3 logiques : soit je n'ai pas la bonne approche à grande échelle, et ca se fait pas d'avoir un pointeur sur un fichier dans une classe.
soit je fais mon remplacement et les vérif éventuelles se font avant
soit dans ce cas, il est IMPOSSIBLE de remplacer le pointeur par une reference.
Je suis à fond pour l'usage des références... le pointeur c'est le mal (pas avant 2 ans d'expérience en C++) !!!!! (Troll ? non, si peu)
Message édité par NounouRs le 01-10-2009 à 11:11:29