5
votes

Comment étendre une structure / classe C ++ avec un élément de type void `null` ou` empty`

J'implémente un module d'échecs pendant que j'apprends le C ++. Dans le module j'ai (partiellement) implémenté:

  • une classe Board (grille, pièces, méthodes de déplacement, etc.)
  • types énumérés Couleur (NOIR / BLANC) et Rang (KING, QUEEN, ..., PAWN)

et

  • une structure Piece regroupant la couleur et le rang (BLACK KING, ..., WHITE PAWN).

Je décide maintenant comment représenter le contenu d'un carré sur le plateau (grille). Un carré doit contenir un Piece (BLACK KING), ou rien. Options envisagées:

  • créer un struct Square contenant deux membres de données, {bool occupé; Piece piece;} , (éventuellement étendre la classe Piece ).

    MON OBJECTION: La solution semble un peu trop lourde, et dans le cas où le carré n'est pas occupé alors le membre piece devrait être vide. Je pense que cela peut entraîner la nécessité d'exceptions pour certaines méthodes de classe, ce qui ne devrait pas être nécessaire.

  • étendre l'énumération de Rank ou Color pour inclure une option vide.

    MON OBJECTION: semble sémantiquement inélégant, hacky et artificiel.

  • recherche dans d'autres packages comme std::optional

    MON OBJECTION: Pour le style de codage et la simplicité, j'aimerais éviter l'utilisation de machines supplémentaires. Est-ce que std :: optional conviendrait même?

  • en utilisant NULL ou nullptr pour représenter l'état d'un carré vide

    MON OBJECTION: Encore une fois, cela semble piraté. Le contenu d'un carré vide n'est PAS NULL , ni le nombre 0 , ne devrait pas être comparable au nombre 26 ... mais devrait être une nouvelle constante EMPTY_SQUARE.

Aucune de ces options ne semble convenir. Existe-t-il un moyen plus élégant d'étendre une classe (comme Piece ) avec d'autres membres (comme EMPTY_SQUARE ) qui n'a aucun des membres de données de la classe d'origine? (Y a-t-il d'autres options?)


6 commentaires

Connexes: stackoverflow.com/questions/2537942/nullable-values-in-c et stackoverflow.com/questions/ 7645578 /…


Pourquoi ne pas créer une classe d'interface pour représenter le contenu carré et dériver une classe Piece ou Empty pour définir le contenu. Ensuite, votre structure Square doit contenir un pointeur vers l'interface. Vous pouvez vérifier plus tard quelle classe "enfant" existe dans le carré


Utilisez std :: optional , c'est exactement le bon outil. Je ne comprends pas votre objection à propos des "mécanismes supplémentaires" à cette approche.


En relation: une autre question sur l'application d'échecs essayant de résoudre un problème similaire.


Cela semble assez basé sur l'opinion. Personnellement, j'opterais pour nullptr parce que soit un carré contient un morceau, soit il n'en contient pas. Donc soit il pointe vers un objet pièce, soit il ne le fait pas. C'est un test facile et ne semble pas surprenant. Je ne considère pas du tout cela comme un "hackey".


@NirMH suggérez-vous d'avoir une classe Square {...} avec un membre de données (public?) Comme bool contains_piece, et une classe dérivée Occupied_square: Square {...}? Cela créera-t-il des problèmes de typage? c'est à dire. Lorsqu'une pièce est déplacée d'un carré, la classe de ce carré ( Occupied_square ) sera rétrogradée à un ancien Square .


4 Réponses :


4
votes

Une case sur votre échiquier peut contenir une pièce ou être vide. En termes plus abstraits, vous voulez avoir quelque chose de spécifique ou rien du tout. C'est exactement le cas d'utilisation pour lequel std :: optional est fait. Vous ne pouvez pas éviter les machines supplémentaires de toute façon car vous avez besoin de quelque chose pour représenter un carré vide. Dans le C ++ de style 2019, un type facultatif est une solution parfaitement simple et idiomatique, en particulier lorsqu'il provient de la bibliothèque standard.

Concernant vos autres alternatives:

  • struct Square : Je suis d'accord avec vos objections, en particulier la question sur ce qu'il faut mettre dans le membre piece lorsque le carré est vide. En bref: struct Square est une version naïve et super simple d'un optionnel.
  • Extension des énumérations: je suis entièrement d'accord.
  • nullptr : Parfois, un pointeur nul peut être une représentation appropriée de «rien». Habituellement, vous rencontrez le problème lorsque votre implémentation fonctionne avec des pointeurs de toute façon et que vous ne voulez pas introduire std::optional - cela introduirait un état de rien-ish supplémentaire avec son ambiguïté . En général, je suis d'accord pour éviter les pointeurs nuls dans la mesure du possible.

0 commentaires

0
votes

Que diriez-vous de conserver une collection de carrés non vides?

typedef std::map<square,piece,chessboard_order,chessboard_allocator> chess_position;

Un allocateur personnalisé est nécessaire car pas plus de 64 carrés sont possibles. De plus, la collection se rétrécit au fur et à mesure que le jeu avance, car des pièces sont prises.


0 commentaires

0
votes

Que diriez-vous que chaque personnage ait une position? Et enregistrez une liste de toutes les figures sur le tableau.

Le tableau aurait la logique associée à la vérification des règles de mouvement.


2 commentaires

Vous voulez dire quelque chose comme stocker 16 figurines blanches et 16 noires, chacune avec 8 pions (comme , ...), 2 évêques () etc. etc. Ce serait une manière intéressante de concevoir l'implémentation, mais pas vraiment dans le cadre de la question.


D'après moi, vous rencontrez un problème avec la représentation d'un état vide. De cette façon, le problème est inexistant.