Forum |  HardWare.fr | News | Articles | PC | S'identifier | S'inscrire | Shop Recherche
1613 connectés 

  FORUM HardWare.fr
  Programmation
  C++

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

 


 Mot :   Pseudo :  
 
Bas de page
Auteur Sujet :

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

n°1928199
NounouRs
Non parce que c pas mon pied !
Posté le 01-10-2009 à 11:09:59  profilanswer
 

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
mood
Publicité
Posté le 01-10-2009 à 11:09:59  profilanswer
 

n°1928204
Joel F
Real men use unique_ptr
Posté le 01-10-2009 à 11:14:34  profilanswer
 

je vois pas le pb ... donne splsu de details sur les type etc la je comprends rien.

n°1928215
NounouRs
Non parce que c pas mon pied !
Posté le 01-10-2009 à 11:27:13  profilanswer
 

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

n°1928218
Joel F
Real men use unique_ptr
Posté le 01-10-2009 à 11:31:06  profilanswer
 

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

n°1928221
Xavier_OM
Monarchiste régicide (fr quoi)
Posté le 01-10-2009 à 12:03:54  profilanswer
 

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.
n°1928299
Joel F
Real men use unique_ptr
Posté le 01-10-2009 à 15:13:10  profilanswer
 

boost::unique_ptr et ca sera parfait :o

n°1928355
NounouRs
Non parce que c pas mon pied !
Posté le 01-10-2009 à 18:05:27  profilanswer
 

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
n°1928383
Joel F
Real men use unique_ptr
Posté le 01-10-2009 à 21:06:42  profilanswer
 

auto_ptr a une semantique moisie

n°1928391
jesus_chri​st
votre nouveau dieu
Posté le 01-10-2009 à 21:59:12  profilanswer
 

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.

n°1931264
Lavock
Posté le 12-10-2009 à 16:21:32  profilanswer
 

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.

mood
Publicité
Posté le 12-10-2009 à 16:21:32  profilanswer
 

n°1931329
Joel F
Real men use unique_ptr
Posté le 12-10-2009 à 21:49:00  profilanswer
 

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

n°1931390
Polo37
Posté le 13-10-2009 à 09:20:12  profilanswer
 

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

n°1931396
Lavock
Posté le 13-10-2009 à 09:51:04  profilanswer
 

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

n°1931401
Polo37
Posté le 13-10-2009 à 09:59:44  profilanswer
 

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)

n°1931406
Lavock
Posté le 13-10-2009 à 10:13:40  profilanswer
 

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

n°1931439
Un Program​meur
Posté le 13-10-2009 à 11:13:31  profilanswer
 

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)
n°1931449
Lavock
Posté le 13-10-2009 à 11:42:18  profilanswer
 

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

n°1931456
Un Program​meur
Posté le 13-10-2009 à 12:04:22  profilanswer
 

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)
n°1931522
Joel F
Real men use unique_ptr
Posté le 13-10-2009 à 15:38:32  profilanswer
 

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 :/

n°1931560
Un Program​meur
Posté le 13-10-2009 à 16:40:46  profilanswer
 

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)
n°1931570
Lavock
Posté le 13-10-2009 à 17:49:03  profilanswer
 

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

n°1931576
Joel F
Real men use unique_ptr
Posté le 13-10-2009 à 18:09:44  profilanswer
 

Il faut que m_file est un constrcteur avec rvalue-ref


Message édité par Joel F le 13-10-2009 à 18:10:14
n°1931603
Lavock
Posté le 13-10-2009 à 21:01:52  profilanswer
 

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 ?

n°1931614
Joel F
Real men use unique_ptr
Posté le 13-10-2009 à 21:21:37  profilanswer
 

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


Message édité par Joel F le 14-10-2009 à 12:14:10
n°1931656
Un Program​meur
Posté le 14-10-2009 à 09:32:18  profilanswer
 

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


---------------
The truth is rarely pure and never simple (Oscar Wilde)
n°1931830
Lavock
Posté le 14-10-2009 à 16:56:46  profilanswer
 

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
n°1931835
Xavier_OM
Monarchiste régicide (fr quoi)
Posté le 14-10-2009 à 17:25:12  profilanswer
 

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.
n°1931836
Lavock
Posté le 14-10-2009 à 17:34:43  profilanswer
 

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
n°1931842
Joel F
Real men use unique_ptr
Posté le 14-10-2009 à 17:50:14  profilanswer
 

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/
 

n°1931891
Un Program​meur
Posté le 14-10-2009 à 20:35:04  profilanswer
 

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)
n°1931892
Joel F
Real men use unique_ptr
Posté le 14-10-2009 à 20:36:16  profilanswer
 

ce code est faux pour la simple raison qu'il leak.
Pour Lavock, il fonctionne car il ne plante pas.

n°1931936
Lavock
Posté le 14-10-2009 à 22:38:31  profilanswer
 

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
n°1931957
Joel F
Real men use unique_ptr
Posté le 15-10-2009 à 06:56:54  profilanswer
 

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.

n°1931966
Lavock
Posté le 15-10-2009 à 08:24:20  profilanswer
 

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

n°1931967
Joel F
Real men use unique_ptr
Posté le 15-10-2009 à 08:26:01  profilanswer
 

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

n°1931970
Lavock
Posté le 15-10-2009 à 08:31:46  profilanswer
 

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

n°1931971
Joel F
Real men use unique_ptr
Posté le 15-10-2009 à 08:36:43  profilanswer
 

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

n°1932002
Un Program​meur
Posté le 15-10-2009 à 10:22:55  profilanswer
 

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)
n°1932650
Glock 17Pr​o
Posté le 16-10-2009 à 23:35:15  profilanswer
 

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


---------------
.
n°1932700
Un Program​meur
Posté le 17-10-2009 à 09:27:15  profilanswer
 

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)
mood
Publicité
Posté le   profilanswer
 


Aller à :
Ajouter une réponse
  FORUM HardWare.fr
  Programmation
  C++

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

 

Sujets relatifs
Tableau de pointeurs en cReferences et objets, bonne pratique
[résolu] parcours d'un char** sans connaitre le nombre de chainesC++ et pointeurs, problème pour désalouer...
soucis pointeursRéférence ou pointeurs?
Cas d'utilisation UMLpointeurs intelligents Boost
Petit problème avec un hash, des tableaux et des référencesTemplates et pointeurs
Plus de sujets relatifs à : Cas où les références remplacent mal les pointeurs ?


Copyright © 1997-2022 Hardware.fr SARL (Signaler un contenu illicite / Données personnelles) / Groupe LDLC / Shop HFR