2011-08-04 7 views
7

Je suis relativement nouveau en C++ et je pense que ma question peut être mieux comprise par l'exemple. Dans mon dossier d'en-tête, supposons que jeGetters-Setters C++ dans le fichier de mise en œuvre

class myClass{ 
    public: 
     double getVar1(); 
     void setVar1(double newVar1); 
     void copyVar1(myClass* dat); 

    private: 
     double var1; 
}; 

Dans mon implémentation fichier .cc, lors de la mise en œuvre de la méthode copyVar1, dois-je faire

void myClass::copyVar1(myClass* dat){ 
    var1 = dat->var1; 
} 

ou

void myClass::copyVar1(myClass* dat){ 
    var1 = dat->getVar1(); 
} 

où dans la deuxième cas, j'utilise la méthode getter à la place. Les deux fonctionnent correctement dans Visual C++, mais je voudrais savoir lequel est préférable d'utiliser dans la pratique.

Merci pour vos commentaires!

+1

Voir aussi [Les fonctions get et set sont-elles populaires auprès des programmeurs C++] (http://stackoverflow.com/questions/737409/are-get-and-set-functions-popular-with-c-programmers) –

Répondre

4

Meilleures pratiques? Surchargez l'opérateur d'affectation au lieu d'écrire une méthode. En ce qui concerne l'utilisation du champ ou l'appel d'un setter ... tout est une question de goût personnel. Si votre getter a un effet secondaire, alors vous devriez probablement l'appeler, sinon, utilisez le champ.

Donc, un grand "dépend".

+1

Mais si la classe a plus d'un membre, l'assignation ne fonctionnera pas car elle devrait copier toutes les valeurs, pas une seule. – Skizz

+0

@Skizz En effet, mais cet exemple n'a qu'un seul champ. –

+1

L'utilisation de * getter * centralise l'opération à un emplacement. C'est un avantage pour la maintenance et le débogage. Le compilateur devrait être assez intelligent pour aligner le getter; sinon, faites des getters et setters en ligne pour aider l'optimisation du compilateur. –

0

Cela dépend. Beaucoup de gens vous diront que l'utilisation des getters/setters au lieu des variables est plus robuste pour les erreurs de programmation et les changements et réduit la duplication du code. Mais tant que ces getters/setters ne sont pas des fonctions en ligne, vous pouvez obtenir un petit coup de performance et comme l'implémentation doit de toute façon connaître les internes de la classe, pourquoi ne pas utiliser directement les variables.

1

Je préfère utiliser setters et getter de cette façon si votre façon de changer certains détails de mise en œuvre (par exemple introduire la validation sur la valeur assign) vous plaisantez doivent changer cela en un seul endroit dans setter ...

+0

Parfois, un getter n'a pas de variable membre correspondante mais est calculé d'une manière ou d'une autre. Dans ces situations, vous devez évidemment utiliser la variable directement. Il n'y a pas de réponse ultime: elle dépend toujours de ce que représente la variable/getter. – ereOn

+0

@ereOn - oui ça dépend ... –

0

Essayez-vous écrire un constructeur de copie? Pourquoi avez-vous besoin de copyVar1 quand vous avez un setVar1? Si vous essayez d'écrire un constructeur de copie, il vaut mieux ne pas utiliser getter.

myclass(const myclass& other) 
{ 
var1 = other.var1; 
} 

Lorsque vous avez à la fois getter et setter, pourquoi ne pas rendre le var1 public?

+0

Il est encore mieux d'utiliser les listes d'initialisation dans les constructeurs, au lieu d'assigner. –

+0

C'était juste un exemple simple. Il y a plus à la méthode copyVar1 que j'ai actuellement implémentée. –

+0

@ A-A: Alors ce que vous voulez est ** copie-affectation **. 'myclass & operator = (const myclass & other);' Regardez la solution @Etienne. – Mahesh

2

Vous devriez presque toujours utiliser les méthodes getter/setter pour accéder à une variable lorsque vous êtes en dehors de la classe, et souvent vous devez le faire car c'est la seule façon de le faire. Cependant, lorsque vous êtes dans la classe, vous pouvez utiliser l'une ou l'autre, et si la méthode getter ne fait rien mais retourne la variable, cela ne fera pas de différence.

Votre décision va reposer sur si vous avez du code dans la méthode de lecture qui fait quelque chose de plus qu'un simple retour de la variable, et si vous voulez que le code à exécuter lorsque copyVar1 est appelé. Si vous ne savez pas, mon conseil serait de toujours utiliser la méthode getter si jamais vous décidez de changer le code dans le futur. Bien que cela fonctionne correctement maintenant en y accédant directement, et que vous ayez de meilleures performances au microscope, il sera beaucoup plus facile de trouver une erreur d'appeler le getter alors que vous ne devriez pas l'appeler quand vous le devriez. Et le compilateur finira probablement par optimiser suffisamment pour ne pas ressentir la différence. : D

+1

La plupart des compilateurs intègrent de toute façon le getter, donc la performance n'est pas le problème ici. –

0

Il n'y a pas de bonne ou de mauvaise façon de le faire ici.

Toutefois, vous supposez que getVar1() renvoie la valeur ou var1. Si vous décidez de changer la façon dont la valeur var1 est stockée, vous devez parcourir tout le code et le mettre à jour. L'utilisation d'un setter/getter réduit ceci à quelques méthodes. Dans ce cas, vous devez utiliser une troisième option:

void myClass::copyVar1(myClass* dat){ 
    setVar1 (dat->getVar1()); 
} 

et vous supprimer complètement la dépendance var1, vous pouvez changer var1 à autre chose et le copyVar1 sera toujours.

0

Comme vous le dites, les deux fonctionnent correctement et sont parfaitement légaux. Dans un sens, la différence entre accéder directement à la variable membre ou utiliser un accesseur est que vous définissez une "couche" plus forte à l'intérieur de votre classe; cette couche, comme n'importe quelle couche, vous aide à réduire les dépendances. En d'autres termes, une méthode getter répond essentiellement à l'exigence de ne pas divulguer au monde extérieur un détail d'implémentation; disons, vous pourriez décider un jour qu'au lieu de stocker var1 en double, vous le calculez. Ainsi, l'accesseur vous donne ce genre de liberté: dans une certaine mesure, vous pouvez modifier l'implémentation sans affecter les autres classes (c'est-à-dire, en réduisant les dépendances).

La même chose est vraie dans le cas de l'utilisation de la méthode getter dans la classe elle-même, c'est-à-dire que vous supprimez des dépendances dans la classe et que votre conception est plus résistante aux modifications.

0

Le point de l'abstraction des setters et getters est que la méthode de récupération peut changer. Lorsque vous accédez directement à la variable membre, si quelque chose change, vous devez mettre à jour votre utilisation partout. Mais si vous utilisez la fonction, changer la façon dont la variable est récupérée doit seulement changer dans la fonction. Maintenant, parce que c'est la même classe, c'est beaucoup moins une préoccupation que lorsque vous mettez des variables publiques, parce que vous devez seulement mettre à jour ce fichier d'implémentation (au lieu de mettre à jour chaque utilisateur de votre classe, qui ne peut pas être possible si vous avez une classe de bibliothèque).

Pour voir ceci, regardez une classe qui doit soudainement devenir thread-safe. Maintenant, votre copieur ressemblerait à

void myClass::copyVar1(myClass * dat) 
{ 
    scoped_lock lock(mutex); 
    var1 = dat->getVar1(); 
} 

et vous n'auriez pas à corriger ailleurs si vous appelez toujours par fonction. Si vous avez accédé à la variable directement, il ressemblerait

void myClass::copyVar1(myClass * dat) 
{ 
    scoped_lock lock(mutex); 
    scoped_lock lock2(dat->mutex) 
    var1 = dat->var1; 
} 

qui bloque « dat » s mutex externe à dat, généralement pas considéré comme un très bon design (si vous faites les ingénieurs doivent se rappeler de verrouiller les objets d'autres personnes alors vous ouvrez la chance qu'ils oublient et ne le font pas). De même, si la variable commence à être stockée dans une base de données, vous devrez également gérer cela. Ou si la variable était maintenant stockée dans deux variables différentes et construite quand elle a été récupérée, vous pouvez voir comment la complexité augmenterait. Mais rappelez-vous, la philosophie de conception de l'utilisation des accesseurs et des mutateurs est de réduire la complexité potentielle grâce à l'encapsulation. Il y a des moments où vous travaillez sur des projets plus petits (en particulier des projets personnels) où vous n'avez pas l'intention de changer la classe. Il pourrait être moins complexe d'y accéder directement. N'ayez pas peur de le faire, sachez simplement pourquoi vous prenez la décision.

+0

Notez également que vous ne devez pas utiliser des constructeurs de copie ou des opérateurs d'affectation pour la même chose que des mutateurs - ils doivent être utilisés à différents endroits. La copie et l'affectation sont utilisées lorsque vous voulez avoir un autre objet comme celui que vous voulez. Les mutateurs sont utilisés lorsque vous voulez pouvoir modifier le comportement d'une manière particulière, sans modifier le reste de l'objet. Vous n'écrivez des mutateurs que s'il y a un autre état qui ne changera pas. La forme de votre mutateur (qu'il prend l'état d'un autre objet existant) ne doit pas impliquer qu'il doit s'agir d'une affectation ou d'une copie. – ex0du5