Question

Quelqu'un m'a parlé d'une différence de style C ++ dans leur équipe. J'ai mon propre point de vue sur le sujet, mais je serais intéressé par les avantages et les inconvénients venant de tout le monde.

Ainsi, si vous souhaitez exposer une propriété de classe à l'aide de deux getters, l'un en lecture / écriture et l'autre en lecture seule (c'est-à-dire qu'il n'y a pas de méthode définie). Il y a au moins deux façons de le faire:

class T ;

class MethodA
{
   public :
      const T & get() const ;
      T & get() ;

      // etc.
} ;

class MethodB
{
   public :
      const T & getAsConst() const ;
      T & get() ;

      // etc.
} ;

Quels seraient les avantages et les inconvénients de chaque méthode?

Je suis plus intéressé par des raisons techniques / sémantiques en C ++, mais les raisons de style sont également les bienvenues.

Notez que MethodB présente un inconvénient technique majeur (indice: en code générique).

Était-ce utile?

La solution

Eh bien, d’une part, getAsConst doit être appelé lorsque le pointeur "this" est const - et non lorsque vous souhaitez recevoir un objet const. Donc, à côté de tout autre problème, il est subtilement mal nommé. (Vous pouvez toujours l'appeler quand 'this' n'est pas constant, mais ce n'est ni ici ni là.)

En ignorant cela, getAsConst ne vous rapporte rien et impose un fardeau excessif au développeur qui utilise l'interface. Au lieu d'appeler simplement & get; get " et sachant qu'il obtient ce dont il a besoin, il doit maintenant déterminer s'il utilise actuellement une variable const et si le nouvel objet qu'il est en train de saisir doit être constant. Et plus tard, si les deux objets deviennent non constants à la suite d’une refactorisation, il doit changer d’appel.

Autres conseils

C ++ devrait être parfaitement capable de gérer la méthode A dans presque toutes les situations. Je l'utilise toujours et je n'ai jamais eu de problème.

La méthode B est, à mon avis, un cas de violation de OnceAndOnlyOnce. Et maintenant, vous devez déterminer si vous utilisez une référence const pour écrire le code compilé pour la première fois.

Je suppose que c'est une chose stylistique - techniquement, ils fonctionnent tous les deux, mais MethodA rend le compilateur un peu plus dur. Pour moi, c'est une bonne chose.

Personnellement, je préfère la première méthode, car elle permet une interface plus cohérente. En outre, pour moi, getAsConst () est aussi bête que getAsInt ().

Sur une note différente, vous devriez vraiment réfléchir à deux fois avant de renvoyer une référence non const ou un pointeur non const à un membre de données de votre classe. C’est une invitation pour les gens à exploiter le fonctionnement interne de votre classe, qui devrait idéalement être caché. En d'autres termes, il casse l'encapsulation. J'utiliserais un get () et un set (), et ne renverrais une référence non-const que s'il n'y a pas d'autre moyen, ou si cela a vraiment du sens, comme de donner un accès en lecture / écriture à un élément d'un tableau ou une matrice.

Etant donné le précédent de style défini par la bibliothèque standard (ie begin () et begin () const pour ne citer qu'un exemple), il devrait être évident que la méthode A est le bon choix. Je remets en question la santé mentale de la personne qui choisit la méthode B.

Ainsi, le premier style est généralement préférable.

Nous utilisons cependant assez souvent une variante du second style dans la base de code sur laquelle je travaille, car nous souhaitons une grande distinction entre les utilisations constantes et non constantes.

Dans mon exemple spécifique, nous avons getTessellation et getMutableTessellation. Il est implémenté avec un pointeur de copie sur écriture. Pour des raisons de performances, nous souhaitons que la version const soit utilisée chaque fois que possible. Nous raccourcissons donc le nom et le rendons différent afin que les personnes ne provoquent pas accidentellement une copie alors qu'elles ne vont pas écrire de toute façon.

Bien qu'il semble que votre question ne concerne qu'une méthode, je me ferai un plaisir de donner mon avis sur le style. Personnellement, pour des raisons de style, je préfère le premier. La plupart des IDE afficheront la signature de type des fonctions pour vous.

Je préférerais le premier. Cela semble mieux dans le code lorsque deux choses qui font essentiellement la même chose se ressemblent. En outre, il est rare que vous ayez un objet non-const, mais que vous souhaitiez appeler la méthode const, ce qui n’a rien de très inquiétant (et dans le pire des cas, vous n’auriez besoin que de const_cast < >).

Le premier autorise les modifications du type de variable (que ce soit const ou non) sans autre modification du code. Bien entendu, cela signifie qu’il n’ya aucune notification au développeur que cela pourrait avoir changé par rapport au chemin prévu. Il s’agit donc vraiment de savoir si vous voulez pouvoir refacturer rapidement ou bénéficier du filet de sécurité supplémentaire.

Le second est quelque chose en rapport avec la notation hongroise que je préfère NE PAS , je vais donc rester avec la méthode first .

Je n'aime pas la notation hongroise car elle ajoute une redondance que je déteste habituellement dans la programmation. C'est juste mon avis.

Puisque vous masquez les noms des classes, cet élément de réflexion sur le style peut ou non s'appliquer:

Est-il judicieux de dire à ces deux objets, MethodA et MethodB, de "obtenir" ou "getAsConst"? Voulez-vous envoyer & get; get " ou " getAsConst " en tant que messages à l'un ou l'autre objet?

À mon avis, en tant qu’expéditeur du message / invocateur de la méthode, vous êtes celui qui reçoit la valeur; donc en réponse à cela & get; get " message, vous envoyez un message à MethodA / MethodB, dont le résultat est la valeur que vous devez obtenir.

Exemple: si l'appelant de MethodA est, par exemple, un service SOA et que MethodA est un référentiel, appelez MethodA.find_A_by_criteria (...) dans le service.

L’inconvénient technologique majeur de MethodB que j’ai vu est que lorsqu’on y applique du code générique, il faut doubler le code pour gérer à la fois les versions const et non-const. Par exemple:

Supposons que T soit un objet pouvant être commandé (c'est-à-dire que nous pouvons comparer des objets de type T avec l'opérateur <), et supposons que nous recherchions le maximum entre deux MethodA (resp. deux MethodB).

Pour MethodA, tout ce dont nous avons besoin de code est:

template <typename T>
T & getMax(T & p_oLeft, T & p_oRight)
{
   if(p_oLeft.get() > p_oRight.get())
   {
      return p_oLeft ;
   }
   else
   {
      return p_oRight ;
   }
}

Ce code fonctionnera à la fois avec les objets const et les objets non const de type T:

// Ok
const MethodA oA_C0(), oA_C1() ;
const MethodA & oA_CResult = getMax(oA_C0, oA_C1) ;

// Ok again
MethodA oA_0(), oA_1() ;
MethodA & oA_Result = getMax(oA_0, oA_1) ;

Le problème survient lorsque nous souhaitons appliquer ce code simple à quelque chose qui respecte la convention MethodB:

// NOT Ok
const MethodB oB_C0(), oB_C1() ;
const MethodB & oB_CResult = getMax(oB_C0, oB_C1) ; // Won't compile

// Ok
MethodA oB_0(), oB_1() ;
MethodA & oB_Result = getMax(oB_0, oB_1) ;

Pour que MethodB fonctionne à la fois avec les versions const et non const, nous devons tous deux utiliser le getMax déjà défini, mais y ajouter la version suivante de getMax:

template <typename T>
const T & getMax(const T & p_oLeft, const T & p_oRight)
{
   if(p_oLeft.getAsConst() > p_oRight.getAsConst())
   {
      return p_oLeft ;
   }
   else
   {
      return p_oRight ;
   }
}

Conclusion, en ne faisant pas confiance au compilateur sur l'utilisation de const-ness, nous nous chargeons de la création de deux fonctions génériques alors qu'une aurait dû suffire.

Bien sûr, avec suffisamment de paranoïa, la deuxième fonction de modèle aurait dû s'appeler getMaxAsConst ... Et ainsi, le problème se propagerait à travers tout le code ...

: - p

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top