web-dev-qa-db-fra.com

Renvoyer une référence de const ou une copie dans une fonction getter?

Quoi de mieux comme défaut, pour renvoyer une copie (1) ou une référence (2) d'une fonction getter?

class foo {
public:
    std::string str () { // (1)
        return str_;
    }

    const std::string& str () { // (2)
        return str_;
    }

private:
    std::string str_;
};

Je sais 2) pourrait être plus rapide mais ne doit pas être à cause de (n) RVO. 1) est plus sûr de faire peser des références, mais l'objet sera probablement survie ou la référence n'est jamais stockée.

Quelle est votre valeur par défaut lorsque vous écrivez une classe et que vous ne savez pas (encore) si la performance et la durée de vie sont importantes?

Question supplémentaire: Le jeu change-t-il lorsque le membre n'est pas une chaîne simple mais plutôt un vecteur?

41
cairol

Eh bien, cela dépend vraiment de ce que vous attendez du comportement, par défaut.

Vous attendez-vous à l'appelant pour voir les modifications apportées à Str_ NoueNiscount (quel mot!) À eux? Ensuite, vous devez transmettre une référence. Pourrait être bon si vous pouvez avoir un membre de données refentéré et retourner cela.

Si vous vous attendez à ce que l'appelant reçoive une copie, faites 1).

22
Aryabhatta

Ma règle générale consiste à renvoyer une copie pour des types de données de base simples tels que INT, String, etc. pour une structure plus compliquée plus compliquée où la copie peut être plus coûteuse (comme le vecteur que vous avez mentionné), je préfère renvoyer une référence de const.

18
Naveen

Le compilateur ne sera pas en mesure d'effectuer (n) RVO dans ce cas. L'optimisation de la valeur de retour (nommée) est une optimisation dans laquelle le compilateur crée la fonction Variables AUTO à la place de la valeur de retour pour éviter de devoir copier:

std::string f()
{
   std::string result;
   //...
   return result;
}

Lorsque le compilateur voit le code ci-dessus (et en supposant que si un autre retour est présent, il renvoie également la variable result) Il sait que la variable result n'a que le seul destin étant copié sur la retourné temporaire puis détruit. Le compilateur peut ensuite retirer la variable result variable et utiliser le retour temporaire comme la seule variable. J'insiste: le compilateur ne supprime pas le retour temporaire, il supprime la variable de fonction locale. Le retour temporaire est tenu de remplir la convention des compilateurs d'appel.

Lorsque vous retournez un membre de votre classe, le membre doit exister et la convention d'appel nécessite que l'objet renvoyé soit dans un endroit particulier (adresse de pile généralement). Le compilateur ne peut pas créer l'attribut de la méthode sur l'emplacement de l'objet renvoyé et ne peut-il pas élier la copie.

Je retourne une référence, car une chaîne ne semble pas "pas cher à copier" pour moi. C'est un type de données complexe avec une gestion de la mémoire dynamique et tout cela.

Le "Si vous voulez que l'appelant reçoive une copie, vous devez retourner par la valeur" L'argument est discutable, car il n'empêche pas du tout des copies. L'appelant peut toujours faire ce qui suit et obtenir une copie quand même

string s = obj.str();

Vous devez créer explicitement une référence sur le côté de l'appelant pour pouvoir faire référence au membre de données directement par la suite - mais pourquoi feriez-vous cela? Il y a certainement suffisamment de types définis par l'utilisateur peu coûteux à copier

  • Pointeurs intelligents
  • Itérateurs
  • Tous les types de classe non-classe.
7

Renvoyer une référence à des internes d'un objet dans le cadre de son interface publique peut être une odeur de code si elle n'est pas définitive de mauvaise conception.

Avant de renvoyer une référence à un objet interne dans une interface publique, le concepteur doit faire une pause. Cela couplait des utilisateurs de votre classe à une partie de votre conception. Souvent, il est absolument inutile, parfois cela indique que d'autres travaux de conception sont nécessaires. Parfois, il est nécessaire, car les commentateurs ont noté.

4
ergosys

S'il n'y a pas de raison particulière d'utiliser une valeur de valeur comme valeur de retour, je retourne toujours une référence de const. Si j'ai besoin (ou si vous m'attendez à avoir besoin) une copie (écritable), j'ajoute une copie CTOR et un opérateur d'affectation à la classe renvoyée si elle n'est pas déjà disponible. Pour l'utilisation pense:

const MyClass & ref = container.GetAt( 1234 ); // need only reference
MyClass copy = container.GetAt( 1234 ); // get writable copy 

En fait, cela est assez simple, n'est-ce pas?

1
ur.

Le seul problème que j'ai avec renvoi d'une const-référence, qui est quelque chose que je ferais généralement pour des types non fondamentaux, est qu'il n'y a rien à empêcher l'appelant en supprimant la ness "const" puis de modifier la valeur.

Personnellement, je suggérerais que ce code est un bug. S'ils savent que vous retournez une référence et continuez à jeter le const, alors c'est sur leur tête.

0
ScaryAardvark
  1. si c'est un petit type de base - des primes de base comme Int et Long et leurs wrappers et autres choses de base comme "point" - renvoyer une copie

  2. si c'est un chaîne ou tout autre type complexe - renvoyez une référence.

0
Will