Cas où les références remplacent mal les pointeurs ?

Cas où les références remplacent mal les pointeurs ? - C++ - Programmation

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.
 

Code :
  1. FileLoader::FileLoader(string filename)
  2. {
  3.     if (!filesystem(filename).isvalid())
  4.     {
  5.         throw Error("invalid filename" );
  6.         return;
  7.     }
  8.     m_pFile = new File(filename);
  9.     // maintenant, on peut faire m_pFile.open(...):
  10. }
  11. FileLoader::~FileLoader()
  12. {
  13.     delete m_pFile;
  14. }


 
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 cité 2 fois
Message édité par NounouRs le 01-10-2009 à 11:11:29
Reply

Marsh Posté le 01-10-2009 à 11:09:59   

Reply

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.

Reply

Marsh Posté le 01-10-2009 à 11:27:13    

La déclaration de classe (bidon) ca peut aider ?  
 

Code :
  1. class FileLoader
  2. {
  3. private:
  4.     File* m_pFile;
  5.     FileLoader(string filename); ///< va ouvrir le fichier et effectuer quelques operation mineurs dessus (j'ai oublié le throw)
  6.     ~FileLoader(); ///< va détruire le fichier
  7. public:
  8.     // tout opération susceptible d'être intéressante sur mon fichier
  9. };


 
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).

Reply

Marsh Posté le 01-10-2009 à 11:31:06    

File c'ets quoi ?
 
Sinon t'as aussi du polymorphisme sur les references hein :€

Reply

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 :
  1. #include<memory>
  2. #include<iostream>
  3. using namespace std;
  4. struct S {
  5.  S() { cout << "make an S\n"; }
  6.  ~S() { cout << "destroy an S\n"; }
  7.  S(const S& ) { cout << "copy initialize an S\n"; }
  8.  S& operator=(const S& ) { cout << "copy assign an S\n"; }
  9. };
  10. S* f()
  11. {
  12.  return new S; // who is responsible for deleting this S?
  13. };
  14. auto_ptr<S> g()
  15. {
  16.  return auto_ptr<S>(new S); // explicitly transfer responsibility for deleting this S
  17. }
  18. int main()
  19. {
  20.  cout << "start main\n";
  21.  S* p = f();
  22.  cout << "after f() before g()\n";
  23. // S* q = g(); // this error would be caught by the compiler
  24.  auto_ptr<S> q = g();
  25.  cout << "exit main\n";
  26.  // leaks *p
  27.  // implicitly deletes *q
  28. }
  

Je dis ptet de la merde hein, faut attendre confirmation de Joel F :o

 

edit : ton exemple rappelle un peu http://www.research.att.com/~bs/bs_faq2.html#finally


Message édité par Xavier_OM le 01-10-2009 à 12:14:51

---------------
Il y a autant d'atomes d'oxygène dans une molécule d'eau que d'étoiles dans le système solaire.
Reply

Marsh Posté le 01-10-2009 à 15:13:10    

boost::unique_ptr et ca sera parfait :o

Reply

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  ??


Message édité par NounouRs le 02-10-2009 à 14:22:21
Reply

Marsh Posté le 01-10-2009 à 21:06:42    

auto_ptr a une semantique moisie

Reply

Marsh Posté le 01-10-2009 à 21:59:12    

un peu HS, mais

Code :
  1. FileLoader::FileLoader(string filename)
  2. {
  3.     if (!filesystem(filename).isvalid())
  4.     {
  5.         throw Error("invalid filename" );
  6.         return;
  7.     }
  8.     m_pFile = new File(filename);
  9.     // maintenant, on peut faire m_pFile.open(...):  
  10. }


 
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. :non:
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.

Reply

Marsh Posté le 12-10-2009 à 16:21:32    

NounouRs a écrit :


Code :
  1. FileLoader::FileLoader(string filename)
  2. {
  3.     if (!filesystem(filename).isvalid())
  4.     {
  5.         throw Error("invalid filename" );
  6.         return;
  7.     }
  8.     m_pFile = new File(filename);
  9.     // maintenant, on peut faire m_pFile.open(...):
  10. }
  11. FileLoader::~FileLoader()
  12. {
  13.     delete m_pFile;
  14. }




 
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.

Reply

Marsh Posté le 12-10-2009 à 16:21:32   

Reply

Marsh Posté le 12-10-2009 à 21:49:00    

Lavock 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.


les liste d'initialsiations font ça très bien hein ...
 

Lavock a écrit :


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.


La seule vrai différence est qu'une ref. n'est jamais nulle, c'est à peu prés tout
 

Lavock a écrit :


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.


Surtout que le vrai coup du File c'ets les ressources systèmes, pas tant la mémoire

Reply

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 :
  1. File check_and_open_file(string const& filename)
  2. {
  3.     if (!filesystem(filename).isvalid())
  4.         throw Error("invalid filename" );
  5.     return File(filename);
  6. }
  7. FileLoader::FileLoader(string const& filename) : m_file(check_and_open_file(filename))
  8. {}


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

Reply

Marsh Posté le 13-10-2009 à 09:51:04    

Joel F a écrit :


les liste d'initialsiations font ça très bien hein ...


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:

Code :
  1. File check_and_open_file(string const& filename)
  2. {
  3.     if (!filesystem(filename).isvalid())
  4.         throw Error("invalid filename" );
  5.     return File(filename);
  6. }
  7. FileLoader::FileLoader(string const& filename) : m_file(check_and_open_file(filename))
  8. {}


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


 
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 :
  1. FileLoader::FileLoader(string const& filename) : m_file(*(new Fille(filename)))
  2. {}


 
ou, si tu tiens vraiment à la vérif :
 

Code :
  1. File& check_and_open_file(string const& filename)
  2. {
  3.     if (!filesystem(filename).isvalid())
  4.         throw Error("invalid filename" );
  5.     return * (new File(filename));
  6. }
  7. FileLoader::FileLoader(string const& filename) : m_file(check_and_open_file(filename))
  8. {}


 
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 :


La seule vrai différence est qu'une ref. n'est jamais nulle, c'est à peu prés tout


 
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 :p).

Reply

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)

Reply

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 &...

Reply

Marsh Posté le 13-10-2009 à 11:13:31    

Joel F a écrit :


La seule vrai différence est qu'une ref. n'est jamais nulle, c'est à peu prés tout


 
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.


---------------
The truth is rarely pure and never simple (Oscar Wilde)
Reply

Marsh Posté le 13-10-2009 à 11:42:18    

Un Programmeur a écrit :


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.


 
erf, je pensais que dans l'absolue on pouvait sortir du code quiche-style :
 

Code :
  1. T operator*(T * t1, T * t2) {
  2.     T  ret();
  3.     ret.membre = (t1->membre)*(t2->membre);
  4.     return ret;
  5. }
  6. ...
  7. T t1();
  8. T t2();
  9. T t3 = &t1 * &t2;


 
Mais bon, même si c'était faisable, bonjour la lisibilité...

Reply

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.


---------------
The truth is rarely pure and never simple (Oscar Wilde)
Reply

Marsh Posté le 13-10-2009 à 15:38:32    

Lavock a écrit :


Oui, mais on ne peut appeler qu'un constructeur par recopie d'un objet non temporaire...


 
En C++0x, je pense que tu fais un std::move.
En C++03 par contre oui :/

Reply

Marsh Posté le 13-10-2009 à 16:40:46    

Lavock a écrit :


Oui, mais on ne peut appeler qu'un constructeur par recopie d'un objet non temporaire...


 
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?


---------------
The truth is rarely pure and never simple (Oscar Wilde)
Reply

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 :


En C++0x, je pense que tu fais un std::move.
En C++03 par contre oui :/


 
Tient, d'ailleurs, on ferait ça comment ?
 

Code :
  1. FileLoader::FileLoader(string const& filename) : m_file(std::move(File(filename))) {}


 
Il me sort un "invalid initialization of non-const reference"...

Reply

Marsh Posté le 13-10-2009 à 18:09:44    

Il faut que m_file est un constrcteur avec rvalue-ref


Message édité par Joel F le 13-10-2009 à 18:10:14
Reply

Marsh Posté le 13-10-2009 à 21:01:52    

En gros, ça en revient à ça :
 

Code :
  1. File& check_and_open_file(string const& filename) {
  2.      if (!filesystem(filename).isvalid())
  3.        throw Error("invalid filename" );
  4.      return * (new File(filename));
  5. }
  6.  
  7. FileLoader::FileLoader(string const& filename) : m_file(std::move(check_and_open_file(filename))) {}


 
Sauf que du coup, la durée de vie sera gérée par le FileLoader ?

Reply

Marsh Posté le 13-10-2009 à 21:21:37    

le new est inutile, renvoit un object creer sur la pile.


Message édité par Joel F le 14-10-2009 à 12:14:10
Reply

Marsh Posté le 14-10-2009 à 09:32:18    

Pire, avec ce code le delete n'est jamais fait.


---------------
The truth is rarely pure and never simple (Oscar Wilde)
Reply

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 :
  1. class B {
  2. private:
  3.   A&& a; // Ca c'est une rvalue ref.
  4. public:
  5.   B(Type param): a(std::move(A(param))) {}
  6. };
 

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  :pt1cable: !

 

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 :
  1. return std::move(ret)

mais attention au bug du runtime !

 

P.P.S : Cas intéressant, changeons le code ci-dessus par :

 
Code :
  1. class B {
  2. private:
  3.   A a;
  4. public:
  5.   B(Type param): a(createA(param))) {} //Appel le move constructor
  6.  
  7.   A&& createA(Type Param) { return *(new A(param)) }
  8. };
 

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 !

Message cité 5 fois
Message édité par Lavock le 14-10-2009 à 22:46:08
Reply

Marsh Posté le 14-10-2009 à 17:25:12    

Lavock a écrit :


[...]
Donc il possible d'initialiser une lvalue ref avec une rvalue ref (qui est une lvalue)
[...]


 
<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 ?


---------------
Il y a autant d'atomes d'oxygène dans une molécule d'eau que d'étoiles dans le système solaire.
Reply

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.


Message édité par Lavock le 14-10-2009 à 17:45:16
Reply

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/
 

Reply

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 :
 

Code :
  1. class B {
  2. private:
  3.   A a;
  4. public:
  5.   B(Type param): a(createA(param))) {} //Appel le move constructor
  6.  
  7.   A&& createA(Type Param) { return *(new A(param)) }
  8. };


 
Marche parfaitement bien ! Qui plus est, cela ne provoque aucune memory leak (essayez un delete a; dans ~B() vous provoquera une superbe runtime-error).


 
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).


---------------
The truth is rarely pure and never simple (Oscar Wilde)
Reply

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.

Reply

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  :o ); 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.
En C++03 par contre oui :/

 

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 :
  1. class B {
  2. private:
  3.   A& a;
  4. public:
  5.   B(Type param): a(*&createA(param))) {}
  6.   A&& createA(Type Param) { return *(new A(param)) }
  7.   ~B(): { delete &a; }
  8. };


(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.


Message édité par Lavock le 14-10-2009 à 22:44:33
Reply

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.

Reply

Marsh Posté le 15-10-2009 à 08:24:20    

Joel F a écrit :

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.


 
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 :


P.S : Une fonction Type&& retournant une rvalue temporaire indique un warning. Il est possible de fourbé le compilateur en mettant un

Code :
  1. return std::move(ret)

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...

Reply

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

Reply

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....

Reply

Marsh Posté le 15-10-2009 à 08:36:43    

oui mais non a cause de la semantique lvalue/rvalue/named rvalue.

Reply

Marsh Posté le 15-10-2009 à 10:22:55    

Pour revenir au probleme initial,
 

NounouRs a écrit :

Code :
  1. FileLoader::FileLoader(string filename)
  2. {
  3.     if (!filesystem(filename).isvalid())
  4.     {
  5.         throw Error("invalid filename" );
  6.         return;
  7.     }
  8.     m_pFile = new File(filename);
  9.     // maintenant, on peut faire m_pFile.open(...):
  10. }
  11. FileLoader::~FileLoader()
  12. {
  13.     delete m_pFile;
  14. }


 
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...


 

Code :
  1. std::string ensureIsValid(string const& filename)
  2. {
  3.    if (filesystem(filename).isvalid()) {
  4.       return filename;
  5.    } else {
  6.       throw Error("invalid filename" );
  7.    }
  8. }
  9. FileLoader::FileLoader(string filename)
  10.     : myFile(ensureIsValid(filename))
  11. {
  12. }


 
et pas besoin de references ni de pointeurs.


---------------
The truth is rarely pure and never simple (Oscar Wilde)
Reply

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 ?


---------------
.
Reply

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 :
  1. FileLoader::FileLoader(string filename)
  2. try {
  3.      : myFile(ensureIsValid(filename))
  4.    {
  5.    }
  6. } catch(...) {
  7. }


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).


---------------
The truth is rarely pure and never simple (Oscar Wilde)
Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed