Getter et setter, pointeurs ou références et bonne syntaxe à utiliser en c ++?

Je voudrais connaître une bonne syntaxe pour les getters et les setters C ++.

private: YourClass *pMember; 

le poseur est facile je suppose:

 void Member(YourClass *value){ this->pMember = value; // forget about deleting etc } 

et le getter? dois-je utiliser des références ou des pointeurs const?

Exemple:

 YourClass &Member(){ return *this->pMember; } 

ou

 YourClass *Member() const{ return this->member; } 

Quelle est la différence entre eux?

Merci,

Joe

MODIFIER:

désolé, je vais modifier ma question … Je connais les références et les pointeurs, je posais des questions sur les références et les pointeurs constants, en tant qu’accesseurs, quelle serait la différence entre eux dans mon code, comme dans le futur, à quoi devrait m’attendre perdre si je vais d’une manière ou d’une autre …

donc je suppose que je vais utiliser des pointeurs const au lieu de références

Les pointeurs const ne peuvent pas être supprimés ou réglés, non?

En droit général:

  • Si NULL est un paramètre valide ou une valeur renvoyée, utilisez des pointeurs.
  • Si NULL n’est PAS un paramètre ou une valeur renvoyé valide, utilisez des références.

Donc, si le setter doit éventuellement être appelé avec NULL, utilisez un pointeur en tant que paramètre. Sinon, utilisez une référence.

S’il est valide d’appeler le getter d’un object contenant un pointeur NULL, il devrait renvoyer un pointeur. Si un tel cas est un invariant illégal, la valeur de retour doit être une référence. Le getter doit alors lancer une exception si la variable membre est NULL.

La meilleure chose à faire est de fournir au client une interface OO réelle masquant les détails de la mise en œuvre. Les Getters et les Setters ne sont pas OO.

Votre code ressemble beaucoup à un langage différent – en C ++, utiliser this->x (par exemple) est relativement inhabituel. Lorsque le code est bien écrit, utilisez un accesseur ou un mutateur.

Bien que je sois assez inhabituel à cet égard, je citerai (encore une fois) en affirmant que forcer le code client à utiliser directement un accesseur ou un mutateur est une mauvaise idée. Si vous avez honnêtement une situation où il est logique que le code client manipule une valeur dans votre object, le code client doit alors utiliser une affectation normale pour lire et / ou écrire cette valeur.

Lorsque / si vous avez besoin de contrôler quelle valeur est assignée, la surcharge d’opérateur vous permet de prendre ce contrôle sans imposer une syntaxe laide get / set sur le code client. Plus précisément, ce que vous voulez, c’est une classe proxy (ou un modèle de classe). Pour ne citer qu’un exemple, l’une des situations les plus courantes où les utilisateurs souhaitent obtenir / définir des fonctions est quelque chose comme un nombre censé être limité à une plage particulière. setXXX vérifie que la nouvelle valeur est dans la plage et getXXX renvoie la valeur.

Si vous le souhaitez, un modèle (assez) simple peut faire le travail beaucoup plus proprement:

 template  > class bounded { const T lower_, upper_; T val_; bool check(T const &value) { return less()(value, lower_) || less()(upper_, value); } void assign(T const &value) { if (check(value)) throw std::domain_error("Out of Range"); val_ = value; } public: bounded(T const &lower, T const &upper) : lower_(lower), upper_(upper) {} bounded(bounded const &init) : lower_(init.lower), upper_(init.upper) { assign(init); } bounded &operator=(T const &v) { assign(v); return *this; } operator T() const { return val_; } friend std::istream &operator>>(std::istream &is, bounded &b) { T temp; is >> temp; if (b.check(temp)) is.setstate(std::ios::failbit); else b.val_ = temp; return is; } }; 

Cela rend également le code beaucoup plus proche de l’auto-documentation – par exemple, lorsque vous déclarez un object tel que: bounded(1, 1024); , il apparaît immédiatement que l’intention est un nombre entier compris entre 1 et 1024. La seule partie que l’on puisse juger discutable est de savoir si 1 et / ou 1024 sont inclus dans l’intervalle. Cela diffère considérablement de la définition d’un int dans la classe et de l’attente de tous ceux qui la regardent se rendent compte qu’ils sont censés utiliser setXXX pour appliquer un ensemble (à ce stade inconnu) de bornes sur les valeurs qui peuvent être atsortingbué.

Lorsque vous en incorporez une dans une classe, vous en faites une variable publique et la plage est toujours appliquée. Dans le code client, il n’y a pas de véritable argument sur la syntaxe – vous affectez simplement une variable publique, comme vous le feriez pour une autre -, avec le détail mineur que tenter d’atsortingbuer une valeur hors de scope génèrera une exception. En théorie, la classe devrait probablement utiliser un paramètre de modèle de politique pour spécifier exactement ce qu’elle fait dans ce cas, mais je n’ai jamais eu de raison réelle de se préoccuper de cela.

Comme d’autres l’ont dit, utilisez des pointeurs si la valeur null est une possibilité.

Dans la plupart des cas, je préfère utiliser des références lorsque cela est possible. Personnellement, dans mon code, j’aime bien utiliser la distinction entre les pointeurs et les références pour signaler la propriété. Je pense aux appels avec des références comme “prêtant” un object à une autre fonction ou classe. La classe d’origine qui a transmis ou renvoyé la référence en est toujours propriétaire et est responsable de sa création, de sa maintenance et de son nettoyage. Lorsque mon code passe un pointeur non-const, cela signifie généralement qu’un transfert ou un partage de propriété est en cours, avec toutes les responsabilités que cela implique.

(Et oui, j’utilise habituellement des pointeurs intelligents. Ceux-ci s’apparentent à des références dans mon esprit. Je parle de code de niveau inférieur à celui-ci.)

Quelle est la différence entre eux?

La référence est un alias de la chose (c’est la chose *). Un pointeur est l’adresse de la chose. S’il y a une chance que ce qui est pointé ne soit pas là, alors vous ne voudrez probablement pas renvoyer de références. Les références disent à l’appelant “Je vais vous donner un pseudonyme qui existera lorsque je vous le renverrai”. En fait, il n’y a vraiment aucun moyen de vérifier la référence pour voir si ce qui est sous-jacent est valide.

Sémantiquement, avec le pointeur, vous impliquez que l’appelant peut vérifier si le membre existe avant de l’utiliser. Habituellement, cela se fait avec un contrôle NULL.

En fin de compte, il n’y a pas de “bonne” réponse. Cela dépend du contrat de la classe et si l’appelant veut / devrait / veut vérifier si “Membre” est toujours là.

La réponse courte est des pointeurs pour des choses qui peuvent être pointées ailleurs et des références pour des alias “non placés”.

+1 sur l’interrogation sur l’utilisation des setters et des getters. Si vous devez les utiliser et avoir la possibilité de nulls, envisagez d’utiliser boost :: shared_ptr. De cette façon, la propriété est gérée pour vous.

En plus des autres réponses, si vous choisissez des références pour le getter, ne l’écrivez pas comme dans votre exemple:

 YourClass &Member(){ return *this->pMember; } 

Votre getter permet en fait de définir, comme dans instance->Member() = YourClass(); et ainsi contourner votre passeur. Cela pourrait ne pas être autorisé si YourClass n’est pas copiable, mais rest une autre chose à garder à l’esprit. Un autre inconvénient est le getter n’est pas const.

Au lieu de cela, écrivez votre getter comme ceci:

 const YourClass &Member() const { return *this->pMember; } 

Jonathan, quel compilateur utilisez-vous? shared_ptr est déjà livré avec cela dans le cadre de l’implémentation TR1 du compilateur.