10
votes

Ne donnez pas vos internes? [C ++]

Je suis en train de lire livre intitulé « C ++ standard de codage » par Herb Sutter, Andrei Alexandrescu et au chapitre 42 de ce livre est un exemple: (chapitre est courte, donc je prends la liberté et coller une partie de celui-ci)

Considérez: p>

 class Socket {
 public:
   // … constructor that opens handle_, destructor that closes handle_, etc. …
   int GetHandle() const {return handle_;} // avoid this - (1) <-why this is bad code?
                                           // and why there is a comment to avoid such code??
 private:
   int handle_; // perhaps an OS resource handle
 };


4 commentaires

Veuillez formater votre code sous forme de code. (Mettez 5 espaces avant chaque ligne de code et la séparez d'une ligne de tous les paragraphes normaux)


Eh bien, je ne comprends pas vraiment comment puis-je formater ce code? Il est formé comme un code - ça ressemble à ça quand même


ATCH, au moment où vous avez posté ce commentaire, Bob Cross avait déjà édité votre question pour formater le code. Regardez la liste des révisions pour voir la différence. Stackoverflow.com/revisions/1924070/List puis parcourez l'aide de la pile de dépassement. Stackoverflow.com/editing-help


Ok, ma faute, je n'ai pas réalisé. Mes excuses. En passant, comme je trouve ce forum Excelent, au contraire, le moyen de formater le texte est juste terrible. Y a-t-il un moyen de l'améliorer à quelque chose de plus inutionné? Très souvent, je passe plus de temps sur le formatage que sur la frappe réelle. C'est assez frustrait. Surtout que j'essaie vraiment de formater ma question de la bonne voie et il y a toujours des "erreurs". Il doit y avoir une meilleure façon.


10 Réponses :


3
votes

1) Ils parlent de retourner une poignée à une prise. Dans de nombreux cas, et INT irait bien (comme la taille d'un tableau ou quelque chose), mais dans ce cas que INT peut être utilisé pour appeler des fonctions C inférieures à la modification de la prise sans la connaissance de votre classe. Tout ce qui permet à la représentation sous-jacente de votre classe de changer sans sa connaissance est une mauvaise conception, comme le dit le chapitre.

2) Je doute qu'ils manquent une REF, pour les raisons indiquées ci-dessus


0 commentaires

4
votes

Le point n'est pas que vous retournez par valeur, ce qui va bien, le point est que vous retournez une poignée de ressource.

Au lieu de cela, votre classe devrait organiser des méthodes qui accèdent à cette ressource et fournissent IO entourant cette ressource.

Si la ressource est un fichier, par exemple, votre classe doit avoir une méthode écrire () et une méthode de lecture () qui lit et écrit sur / depuis le fichier.


0 commentaires

32
votes

Je pense que ce qui vous manque, c'est qu'une poignée - même si elle est représentée par un INT dans le système de type - est une référence à quelque chose. Cela ne renvoie pas une valeur d'information - elle renvoie la référence interne de l'objet à une ressource système. La classe devrait gérer cette poignée elle-même et la poignée doit être influencée par le monde extérieur uniquement à travers l'interface de la classe.


1 commentaires

Dans les mots de mon cousin Vinny, «c'est une explication lucide, intelligente et bien pensée», explication. +1



0
votes

Le point fait par l'exemple est que la poignée à une prise est renvoyée, même si vous le soulignez. Une fois que l'appelant a la poignée, il peut l'utiliser pour effectuer des appels de système sans passer à travers la couche d'abstraction fournie par votre classe.


0 commentaires

9
votes

Le problème n'est pas avec les détails de faible niveau (le code est parfaitement bon C ++ de cette façon).

Le problème est que vous enfreignez votre abstraction. Et si dans le futur, au lieu d'avoir une poignée int, vous devez changer cela à une sorte de pointeur. Vous ne seriez pas en mesure de faire ce changement sans casser aucun client qui utilise votre classe.


2 commentaires

En outre, en renvoyant une sorte de poignée à l'appelant, l'appelant pourrait faire quelque chose d'inattendu comme près de la prise sous-jacente. La classe ne serait pas au courant que cela s'était passé et essaierait alors d'accéder à une poignée qui avait déjà été fermée.


Samuel, bien que soit un problème potentiel avec le code, ce n'est pas le problème que Sutter et Alexandrescu utilisaient ce code pour illustrer.



1
votes

Vous êtes correct lorsque vous dites que vous dirigez des références et des pointeurs à des membres privés sont une mauvaise pratique, mais ici, il est également mauvais de renvoyer la valeur car la valeur a une signification interne. Une poignée peut être considérée comme une adresse à un objet, une adresse d'un objet géré au fond du système d'exploitation.

Permettre aux classes externes d'accéder à cette poignée va être une mauvaise nouvelle. Imaginez que c'est une poignée de fichier. Une classe externe peut désormais fermer votre fichier (connaissant sa poignée) et votre classe d'emballage ne saura rien à ce sujet. Les internes de votre classe sont maintenant dans un état invalide.


0 commentaires

4
votes

Remarque la déclaration de Poignée _ : xxx

Même si vous retournez une INT par la valeur du point de vue de C ++, du point de vue du système d'exploitation que la poignée est une "référence" à une ressource OS.


0 commentaires

1
votes

Le code doit révéler des informations qui ne sont pas nécessaires pour l'OMS utilise la classe, et il devrait rendre l'interface de classe plus indépendante de la mise en œuvre réelle.
Nécessitant un gestionnaire de ressources est généralement un moyen d'écrire moins de code et rend la classe plus liée à la mise en œuvre, plutôt que de le rendre dépendant de l'abstraction.


0 commentaires

1
votes

Voici l'arrière-plan sur les poignées en général:

http://www.anvir.com/handle.htm

Les poignées sont des références opaques aux ressources (c'est-à-dire l'emplacement de la mémoire) et seul le sous-système qui vous a donné la poignée sait comment la poignée est associée à un pointeur physique. Ce n'est ni une valeur ni un pointeur ni référence, ce n'est qu'un alias pour une ressource que vous utilisez avec l'API qui sait quoi avec elle.

Alors, quel livre essaie de dire que lorsque vous avez une classe qui gère une certaine ressource, vous ajoutez une couche d'abstraction. Toutefois, si vous donnez une poignée à une ressource, vous ne résumez vraiment pas la mise en œuvre, car votre abstraction peut être facilement contournée.

Une exigence d'avoir des poignées et des fonctions qui prennent des poignées, car les paramètres d'exécution de certaines tâches sont principalement dictés par des langages procéduraux tels que C, qui ne disposent pas d'objets et ne peuvent donc pas cacher une certaine ressource à l'intérieur d'une classe et ne vous fourniraient que des méthodes. opérer sur cette ressource.

Un exemple de ceci pourrait être la bibliothèque Microsoft MFC C ++ où CWND Classe a un accesseur qui renvoie le HWND (I.E. HANDES):

http://msdn.microsoft.com/ EN-US / Bibliothèque / D64EHWHZ (vs.71) .aspx


0 commentaires

1
votes

"état mutable partagé".

En faisant passer la poignée, l'API crée un état mutable partagé, qui est quelque chose qui devrait être évité dans la mesure du possible.

Considérons une API alternative, qui a exposé une méthode proche () plutôt que de Gethandle (). En n'exposant jamais la poignée directement, la classe garantit ensuite que ce sera le seul à fermer la poignée. La poignée devient l'état privé de la classe.

Cela peut sembler comme si cela boufferait votre API, mais il y a un réel avantage - la classe saura alors que toute modification apportée à l'État l'a traversée. Dans la mise en œuvre existante, il doit vérifier le statut de la poignée à chaque fois qu'il fait quoi que ce soit, car cela ne possède pas cet état. En masquant la poignée, la classe se débarrasse de ce cas d'erreur entièrement.


0 commentaires