71 votes

Style de codage getters/setters C++

Je programme en C# depuis un certain temps et je veux maintenant rafraîchir mes connaissances en C++.

Avoir la classe :

class Foo
{
    const std::string& name_;
    ...
};

Quelle serait la meilleure approche (je veux seulement autoriser l'accès en lecture au champ name_) :

  • utiliser une méthode getter : inline const std::string& name() const { return name_; }
  • rendre le champ public puisqu'il s'agit d'une constante

Gracias.

0 votes

0 votes

Soit dit en passant, il est plus courant d'utiliser un seul trait de soulignement en tête pour les variables membres en C++.

1 votes

Je pense également que le trait de soulignement à la fin est un peu gênant, mais c'est ce que j'ai vu être utilisé dans le C++ Faq Lite.

81voto

j_random_hacker Points 28473

L'utilisation d'une méthode getter est un meilleur choix de conception pour une classe à longue durée de vie, car elle vous permet de remplacer la méthode getter par quelque chose de plus compliqué à l'avenir. Bien que cela semble moins nécessaire pour une valeur constante, le coût est faible et les avantages possibles sont importants.

Par ailleurs, en C++, c'est une très bonne idée de donner à la fois le getter et le setter d'un membre. le même nom puisque, à l'avenir, vous pourrez effectivement modifier la paire de méthodes :

class Foo {
public:
    std::string const& name() const;          // Getter
    void name(std::string const& newName);    // Setter
    ...
};

dans une variable membre unique et publique qui définit l'un des éléments suivants operator()() pour chacun :

// This class encapsulates a fancier type of name
class fancy_name {
public:
    // Getter
    std::string const& operator()() const {
        return _compute_fancy_name();    // Does some internal work
    }

    // Setter
    void operator()(std::string const& newName) {
        _set_fancy_name(newName);        // Does some internal work
    }
    ...
};

class Foo {
public:
    fancy_name name;
    ...
};

Le code client devra bien sûr être recompilé, mais aucun changement de syntaxe n'est nécessaire ! Évidemment, cette transformation fonctionne tout aussi bien pour les valeurs constantes, pour lesquelles seul un getter est nécessaire.

2 votes

Hé, j'ai trouvé votre réponse avant de poster ma question... elle semble être presque exactement la même et il semble que vous proposiez le type de solution que je recherche mais étant un noob je ne peux pas être sûr. Pourriez-vous jeter un coup d'oeil à ma question ici : stackoverflow.com/questions/8454887/ et me faire savoir si cette réponse est sur la bonne voie pour ce que j'aimerais accomplir ?

1 votes

Si vous étiez confus comme moi : il n'y a pas de operator()() . Il existe un operator() avec une liste de paramètres.

0 votes

Le remplacement par un membre public ne romprait-il pas l'encapsulation en permettant de contourner le getter/setter ?

49voto

Mr Fooz Points 21092

C'est généralement une mauvaise idée de rendre publics les champs non constants, car il devient alors difficile de forcer les contraintes de vérification des erreurs et/ou d'ajouter des effets secondaires aux changements de valeur à l'avenir.

Dans votre cas, vous avez un champ de type "const", donc les problèmes ci-dessus ne se posent pas. Le principal inconvénient d'en faire un champ public est que vous verrouillez l'implémentation sous-jacente. Par exemple, si à l'avenir vous vouliez changer la représentation interne en une chaîne C ou une chaîne Unicode, ou autre chose, alors vous casseriez tout le code client. Avec un getter, vous pouvez convertir l'ancienne représentation pour les clients existants tout en fournissant la nouvelle fonctionnalité aux nouveaux utilisateurs via un nouveau getter.

Je suggère toujours d'avoir une méthode getter comme celle que vous avez placée ci-dessus. Cela maximisera votre flexibilité future.

1 votes

Si je passe d'une fonction qui renvoie un std::string à une fonction qui renvoie une chaîne Unicode, je casse déjà tout le code client.

6 votes

Et si votre représentation interne était Unicode, mais que vous pouviez la convertir en UTF8 pour assurer la compatibilité avec les clients existants ? Une méthode getter pourrait effectuer la conversion, mais un champ public interdirait ce modèle.

0 votes

@JBRWilkinson me semble être un cas où l'on pourrait vouloir des getters de noms différents pour les variantes Unicode et UTF8. Si la représentation interne reste la même mais que le type de récupérateur change, vous rompez la compatibilité ; le seul intérêt de cette solution est que vous pensez que la représentation interne va changer.

24voto

chrish Points 1208

Soit dit en passant, en C++, il est quelque peu étrange d'avoir un membre de référence constant. Vous devez l'assigner dans la liste du constructeur. Qui possède la mémoire réelle de cet objet et quelle est sa durée de vie ?

Pour ce qui est du style, je suis d'accord avec les autres pour dire que vous ne voulez pas exposer vos parties intimes :-) J'aime ce modèle pour les setters/getters.

class Foo
{
public:
  const string& FirstName() const;
  Foo& FirstName(const string& newFirstName);

  const string& LastName() const;
  Foo& LastName(const string& newLastName);

  const string& Title() const;
  Foo& Title(const string& newTitle);
};

De cette façon, vous pouvez faire quelque chose comme :

Foo f;
f.FirstName("Jim").LastName("Bob").Title("Programmer");

3 votes

Bien qu'il se lise et s'écrive bien, le style API de chaînage de méthodes ("fluide") entraîne une certaine surcharge : stackoverflow.com/q/3134416/102345

0 votes

@chrish Je travaille avec C++ depuis de nombreuses années, mais je n'ai jamais pensé à le faire de cette façon. Quel est le nom de ce style getter/setter ?

1 votes

@BufferOverflow c'est ce qu'on appelle le style fluent api. Il est lié au modèle de construction de Java.

5voto

Dan Breslau Points 9217

Même si le nom est immuable, vous pouvez toujours vouloir avoir la possibilité de le calculer plutôt que de le stocker dans un champ. (Je réalise que c'est peu probable pour "nom", mais visons le cas général). Pour cette raison, même les champs constants sont mieux enveloppés dans des getters :

class Foo {
    public:
        const std::string& getName() const {return name_;}
    private:
        const std::string& name_;
};

Notez que si vous changez getName() pour renvoyer une valeur calculée, il ne pouvait pas renvoyer const ref. Ce n'est pas grave, car cela ne nécessitera aucun changement pour les appelants (modulo recompilation.)

1 votes

Permettez-moi donc de résumer cette réponse : 1) il est hautement improbable que vous changiez un jour l'implémentation de getName() parce que... bon sang, les données sont juste stockées, comment diable allez-vous les recalculer à chaque fois qu'on y accède ? Vous utilisez le C++ pour les performances, n'est-ce pas ? et 2) en fin de compte, même si vous trouvez un cas d'utilisation aussi fou, vous ne serez pas en mesure de le faire, parce qu'il est impossible de retourner une référence constante sans déclencher un comportement indéfini. Êtes-vous déjà convaincu ?

0 votes

Ulidtko, d'après votre "ton de voix" ici, je doute qu'une réponse que je vous donne puisse vous apaiser. Cependant, permettez-moi de souligner une chose : j'ai écrit " Notez que si vous deviez changer getName() pour qu'il renvoie une valeur calculée, il ne pourrait pas renvoyer const ref. Ce n'est pas grave, car cela ne nécessitera aucun changement pour les appelants (modulo recompilation.) " En d'autres termes, il faudrait changer la méthode de const std::string& getName() const a std::string getName() const . Bien entendu, la modification d'une fonction en ligne ou d'un champ nécessite toujours une recompilation, ce qui n'augmente pas la charge de travail à la compilation.

0 votes

Votre note ici est correcte. Vous pourriez aussi profiter de quelques conseils intelligents, probablement. Cependant, il est vrai que j'ai une opinion sur les getters et setters en C++, et que je rassemble des arguments pour et contre.

3voto

David Thornley Points 39051

Évitez les variables publiques, sauf pour les classes qui sont essentiellement des structs de style C. Ce n'est tout simplement pas une bonne pratique à adopter.

Une fois que vous avez défini l'interface de la classe, il se peut que vous ne puissiez jamais la modifier (à part la compléter), car les gens se baseront sur elle et s'y fieront. Rendre une variable publique signifie que vous devez disposer de cette variable, et que vous devez vous assurer qu'elle possède ce dont l'utilisateur a besoin.

Maintenant, si vous utilisez un getter, vous promettez de fournir certaines informations, qui sont actuellement conservées dans cette variable. Si la situation change, et que vous préférez ne pas maintenir cette variable en permanence, vous pouvez modifier l'accès. Si les exigences changent (et j'ai vu des changements d'exigences assez étranges), et que vous avez surtout besoin du nom qui est dans cette variable mais parfois de celui qui est dans cette variable, vous pouvez simplement changer le getter. Si vous rendiez la variable publique, vous seriez coincé avec elle.

Cela n'arrivera pas toujours, mais je trouve beaucoup plus facile d'écrire un getter rapide que d'analyser la situation pour voir si je regretterais de rendre la variable publique (et risquer de me tromper plus tard).

Rendre les variables membres privées est une bonne habitude à prendre. Tout magasin qui a des normes de code va probablement interdire de rendre publique une variable membre occasionnelle, et tout magasin qui a des revues de code va probablement vous critiquer pour cela.

Chaque fois que cela n'a pas d'importance pour la facilité d'écriture, prenez l'habitude d'être plus sûr.

0 votes

J'ai tendance à ne pas être d'accord, parce qu'avec un champ public, la situation de "changement d'exigences" n'est pas si fatale : vous pouvez toujours utiliser des modèles, l'héritage et des operator= pour simuler une propriété en C++. Et en fait, la plupart du temps, vous n'en aurez pas besoin (sérieusement, combien de fois avez-vous fait des getters ou setters non triviaux ?), alors que la syntaxe client de get() / set() est juste moche.

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