54 votes

Le renvoi des références des variables membres est-il une mauvaise pratique ?

On dit que ce qui suit est mieux que d'avoir First() et Second() comme membres publics. Je pense que c'est presque aussi mauvais.

// Example 17-3(b): Proper encapsulation, initially with inline accessors. Later
// in life, these might grow into nontrivial functions if needed; if not, then not.

template<class T, class U>
class Couple {
public:
  Couple()           : deleted_(false) { }
  void MarkDeleted() { deleted_ = true; }
  bool IsDeleted()   { return deleted_; }

private:
 T first_;
 U second_;
 bool deleted_;
 T& First()         { return first_; }
 U& Second()        { return second_; }
};

Si vous donnez un moyen d'accéder à une variable privée en dehors de la classe, alors quel est l'intérêt ? Les fonctions ne devraient-elles pas être

T First(); void(or T) First(const T&)

63voto

Matthieu M. Points 101624

Il y a plusieurs raisons pour lesquelles il est mauvais de renvoyer des références (ou des pointeurs) vers les internes d'une classe. En commençant par (ce que je considère être) la plus importante :

  1. Encapsulation est violée : vous divulguez un détail d'implémentation, ce qui signifie que vous ne pouvez plus modifier les internes de votre classe comme vous le souhaitez. Si vous avez décidé de ne pas stocker first_ par exemple, mais pour le calculer à la volée, comment retourner une référence à celui-ci ? Vous ne pouvez pas, donc vous êtes coincé.

  2. Invariant ne sont plus viables (dans le cas d'une référence non-const) : n'importe qui peut accéder et modifier à volonté l'attribut auquel il est fait référence, vous ne pouvez donc pas "surveiller" ses changements. Cela signifie que vous ne pouvez pas maintenir un invariant dont cet attribut fait partie. Essentiellement, votre classe se transforme en un blob.

  3. À vie Des problèmes surgissent : il est facile de conserver une référence ou un pointeur vers l'attribut après que l'objet original auquel ils appartiennent a cessé d'exister. Il s'agit bien entendu d'un comportement non défini. La plupart des compilateurs essaieront de mettre en garde contre la conservation de références à des objets sur la pile, par exemple, mais je ne connais aucun compilateur qui ait réussi à produire de tels avertissements pour les références renvoyées par des fonctions ou des méthodes : vous devez vous débrouiller tout seul.

En tant que tel, il est généralement préférable de ne pas donner de références ou de pointeurs vers des attributs. Pas même les constantes !

Pour les petites valeurs, il est généralement suffisant de les passer par copie (les deux in y out ), surtout maintenant avec la sémantique du mouvement (à l'entrée).

Pour des valeurs plus importantes, cela dépend vraiment de la situation, parfois un Proxy peut vous soulager.

Enfin, notez que pour certaines classes, avoir des membres publics n'est pas si mal. Quel serait l'intérêt d'encapsuler les membres d'une classe de type pair ? Lorsque vous vous retrouvez à écrire une classe qui n'est rien de plus qu'une collection d'attributs (sans aucun invariant), au lieu de faire du OO et d'écrire une paire getter/setter pour chacun d'eux, pensez à les rendre publics.

32voto

iammilind Points 29275

Si template types T y U sont de grandes structures, alors le retour par valeur est coûteux . Cependant, vous avez raison de dire que le retour par référence équivaut à donner accès à un fichier de type private variable. Pour résoudre les deux problèmes, faites-les const références :

const T& First()  const  { return first_; }
const U& Second() const  { return second_; }

P.S. De plus, c'est une mauvaise pratique de garder les variables non initialisées dans le constructeur, lorsqu'il n'y a pas de méthode setter. Il semble que dans le code original, First() y Second() sont des enveloppes sur first_ y second_ qui étaient destinés à la lecture et à l'écriture.

9voto

Ramesh Kadambi Points 371

La réponse dépend de ce que l'on cherche à faire. Les références retournées sont un moyen pratique de faciliter la mutation des structures de données. Un bon exemple est la carte stl. Elle renvoie une référence à l'élément, à savoir

std::map<int,std::string> a;
a[1] = 1;

rien ne vous empêche de faire

auto & aref = a[1];

S'agit-il nécessairement d'une mauvaise pratique ? Je ne le pense pas. Je dirais que si vous pouvez vous en passer, faites-le. Si cela rend la vie plus pratique et plus efficace, utilisez-le, mais soyez conscient de ce que vous faites.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X