Existe-t-il un moyen approprié de renvoyer une nouvelle instance d’object par référence en C ++?

Donc, j’écrivais du code, et j’avais quelque chose comme ça:

class Box { private: float x, y, w, h; public: //... Rectangle & GetRect( void ) const { return Rectangle( x, y, w, h ); } }; 

Puis plus tard dans un code:

 Rectangle rect = theBox.GetRect(); 

Ce qui a fonctionné dans ma version de débogage, mais dans l’édition, il y avait des “problèmes” renvoyant ce rectangle par référence – j’ai en gros un rectangle non initialisé. La classe Rectangle a un opérateur = et un constructeur de copie. Sans entrer dans les détails, je suis en fait plus intéressé par la manière correcte de renvoyer un (nouvel) object par référence dans le but d’ atsortingbuer la copie à une variable. Suis-je juste bête? Cela ne devrait-il pas être fait? Je sais que je peux renvoyer un pointeur et ensuite déréférencer la cession, mais je préfère ne pas. Une partie de moi-même pense que retourner par valeur entraînerait une copie redondante de l’object – le compilateur le détecte-t-il et l’optimise-t-il?

Cela semble être une question sortingviale. Je me sens presque gêné. Je ne le sais pas après de nombreuses années de codage C ++. J’espère donc que quelqu’un pourra résoudre ce problème pour moi. 🙂

Vous ne pouvez pas renvoyer de référence à un object temporaire de la stack. Vous avez trois options:

  1. Le retourner par valeur
  2. Renvoyez par référence via un pointeur à quelque chose que vous avez créé sur le tas avec l’opérateur new.
  3. Renvoyez par référence ce que vous avez reçu par référence sous forme d’argument. [EDIT: Merci à @ harshath.jr pour l’avoir signalé]

Notez que lorsque vous renvoyez par valeur comme dans le code ci-dessous, le compilateur doit optimiser l’affectation pour éviter la copie. En d’autres termes, il ne créera qu’un rectangle unique (rect) en optimisant la création + l’assignation + la copie dans une création. Cela ne fonctionne que lorsque vous créez le nouvel object lorsque vous revenez de la fonction.

 Rectangle GetRect( void ) const { return Rectangle( x, y, w, h ); } Rectangle rect = theBox.GetRect(); 

Non, vous ne pouvez pas faire ça. Dans cet exemple, vous essayez essentiellement de renvoyer une référence à une variable temporaire de la stack. Au moment où la référence est renvoyée, la variable à laquelle elle pointe est détruite et la référence n’est donc pas valide.

Retourner un object par valeur (voir exemple ci-dessous) peut en fait être moins coûteux que vous ne le pensez. Le compilateur optimise souvent la copie supplémentaire. C’est ce qu’on appelle l’ optimisation de la valeur de retour .

  Rectangle GetRect( void ) const { return Rectangle( x, y, w, h ); } 

Vous pouvez être dérouté par le concept de la durée de vie d’un temporaire. Considérer:

 void f1( const A & a ) { } A f2() { return A; } f1( f2() ); 

C’est du code OK, et la norme dit que le nom temporaire sans nom créé par f2 doit être suffisamment long pour être utilisable dans f1.

Cependant, votre cas est quelque peu différent. Ce que votre fonction retourne est une référence, et par conséquent, le temporaire sans nom est également une référence. Cette référence doit être suffisamment longue pour être utile, mais la chose à laquelle elle se réfère n’est pas nécessaire.

  • Vous pouvez soit renvoyer une référence aux entrailles de votre classe Box (avoir un membre Rectangle . Il est conseillé de renvoyer une référence const ).
  • ou retournez simplement un Rectangle . Notez que l’utilisation de l’idiome return SomeClass(a,b,c); déclenchera probablement une optimisation de la valeur de retour (RVO) sur un compilateur décent.

Vérifiez votre implémentation std::complex pour plus de détails.

Existe-t-il un moyen approprié de renvoyer une nouvelle instance d’object par référence en C ++?

Non, pas par référence. Il existe deux manières de créer un nouvel object:

Sur la stack:

 Rectangle makeRect() { return Rectangle(x, y, w, h); } Rectangle r = makeRect(); // return by value 

Sur le tas:

 Rectangle * makeRect() { return new Rectangle(x, y, w, y); } Rectangle * r = makeRect(); // returned a pointer, don't forget to delete it later 

Pourquoi pas quelque chose comme ça?

 class Box { private: Rectangle mRectangle; public: Box(float x, float y, float w, float h) : mRectangle(x, y, w, h) // Forgive me for making assumptions // about the inner workings of your // code here. { } const Rectangle & GetRect() const { return mRectangle; } }; Rectangle rect = theBox.GetRect(); 

La «mission» devrait fonctionner maintenant. (Techniquement, il ne s’agit pas d’un opérateur d’affectation, mais d’un constructeur de copie appelé.)

Dans l’espoir d’aider

Ce n’est pas possible. Une référence est une autre forme de pointeur et vous retournez en fait l’adresse d’un object qui aura été détruit (appelé destructeur) et éventuellement même écrasé au moment où l’appelant aura pris le contrôle.

Tu peux soit

  • appeler new et renvoyer un pointeur (peut-être devriez-vous penser à un pointeur intelligent) vers un object ou
  • retourne en valeur ou
  • passer l’object par référence dans une fonction afin qu’il le remplisse.

Si le rectangle au sens binary ressemble à la boîte, c’est-à-dire composée de quatre flottants (mais avec des fonctions de membre différentes), vous pouvez utiliser un reinterpret_cast , bien que je ne le recommande pas du tout:

  const Rectangle & GetRect( void ) const { assert(sizeof(Rectangle) == sizeof(Box)); return reinterpret_cast  (*this); } 

nous pouvons utiliser auto_ptr, si nous voulons utiliser new et sans risque de Fuite de mémoire

 class Box { private: float x, y, w, h; public: //... std::auto_ptr GetRect( void ) const { return std::auto_ptr ( new Rectangle( x, y, w, h )); } };