J'essaie de trouver une belle solution de héritage en C ++.
J'ai une classe rectangle et une classe carrée. La classe Square ne peut pas hériter publiquement du rectangle, car elle ne peut pas remplir complètement les exigences du rectangle. Par exemple, un rectangle peut avoir sa largeur et une hauteur de la hauteur de chaque ensemble séparément, et cela est bien sûr impossible avec un carré. P>
Donc, mon dilemme. Square partagera évidemment beaucoup de code avec rectangle; Ils sont assez similaires. p>
pour examllpe, si j'ai une fonction comme: p> Il devrait également fonctionner pour un carré. En fait, j'ai une tonne de telles fonctions. P> Donc, dans la fabrication de ma classe carrée, j'ai pensé que j'utiliserais héritage privé avec un opérateur de conversion rectangulaire accessible au public. Donc, ma classe carrée ressemble à: p> Cependant, lorsque j'essaie de passer un carré à la fonction IspointInrectangle, mon compilateur se plaint de ce "rectangle est une base inaccessible" en ce sens que le contexte. Je m'attends à ce qu'il remarque l'opérateur de rectangle et à utiliser cela à la place. P> est ce que j'essaie de faire même possible? P> Si cela ne peut pas fonctionner, je ne vais probablement pas marcher Refacteur Partie du rectangle dans MutaBerectangle classe. P> Merci. p> p>
3 Réponses :
Vous pouvez créer une classe L'introduction mutabilité le long de l'héritage est correcte, tant qu'aucun invariance de classe de la base (immuable) réellement net em> sur la caractéristique de l'immutabilité; Et bien sûr, un objet immuable peut être correctement construit à partir d'un pointeur EDIT STRUT>: Un commentaire exprime de manière compréhensible les scrupules car "une mutabe n'est pas immuable": pour raisonner à ce sujet, vous devez comprendre ce que "est-a" Peut-être que cela aiderait à nommer la classe de base immutablérectangle code>, sans aucun mutateur et avec uniquement des méthodes
const code>, à partir desquelles vous pouvez calculer correctement les deux
rectangle code>, et , séparément,
immutablesQuare code> et, à partir de celui-ci,
carré code>. Notez que, thannie d'une mutabilité, le
est-a-a-relation code> fort> fait strud> est-un carré immuable est-un rectangle immuable: une mutabilité est le seul problème grave, donc en l'emportant Vous pouvez obtenir une nouvelle réutilisation de code substantielle (pour tous les
const code> utilise - ceux qui n'utilisent pas, ni besoin, mutabilité). p>
const code> ou de référence à la version mutable (probablement dans une fonction d'ami en ligne distincte pour éviter de donner une dépendance de la classe de base sur la classe dérivée ;-) pour une utilisation raisonnablement pratique. P>
est code> de l'identité ": cela signifie le LSP . Passez à travers le rigueur des contraintes que cela signifie: covariance, contravariace, faibles conditions préalables, postconditions plus fortes, etc., comme ils s'appliquent aux méthodes
notnyentailymutablerectangle code> car il ne affirme em> immutabilité comme invariant de classe; que la nommée très précise pourrait être philosophiquement rassurante, mais peut-être une bagatelle non pratique dans le codage quotidien. P>
Cela peut être "ok" - j'ai certainement donné un +1 - mais c'est un peu déconcertant d'avoir le principe "Isa" brisé comme ça. Une mutable la plus définitive isnota immutable.
@Steve, mais le point est, comment le principe de substitution de Liskov serait-il cassé si vous avez passé dans un carré code> où l'arg est
const immutablérectangle & x code>? Ce ne serait pas - la callee n'appelle que des méthodes (const) sur
x code>, et tout fonctionne. que b> est ce que ISA signifie b> dans OOP - pas "le cas d'identité". Je vais éditer la réponse pour annoncer cette clarification ... TX pour exprimer vos doutes!
Je ne suis pas sûr de besoin d'une édition - vous avez déjà mentionné des invariants. Mon point est, je suppose que cette intuition et que le principe théorique ne correspond pas toujours exactement. Une bonne approche peut encore parfois être un peu déconcertante. Dans un autre contexte, l'héritage comme s'étendant plutôt que d'Isa aurait été beaucoup moins de jarring - mais il y a juste quelque chose à propos de formes, probablement à cause de toutes ces exemples de salle de classe et de manuels.
OTOH - Cela pourrait être un problème de nom. MutaBerectangle ISA Rectangle, et il n'y a aucune raison pour laquelle le rectangle ne peut pas avoir toutes les fonctionnalités immuables construites. Pour moi, c'est moins de jarre.
@Steve, vous pouvez avoir raison de la nommer - peut-être que les noms de base des classes représentant des entités mathématiques telles que des formes devraient impliquer i> immutabilité au moins dans le sens de "pas nécessairement mutable" (plus mathématiquement et philosophiquement Traitable) et un préfixe code> mutable indiquent spécifiquement les classes mutables.
"Une bagatelle non pratique" une bagatelle pas nécessairement - pratique? L'ambiguïté est juste entre "immuable", ce qui signifie "Cet objet ne permet pas le changement" et "immuable", ce qui signifie "cette interface i> à l'objet ne permet pas le changement". Si un joueur de certains sports est "imparable", cela ne signifie pas qu'elle ne s'arrête jamais, mais en contraste "données immuables" signifie généralement que cela ne peut changer par aucun moyen. J'aime la solution de rectangle code> étant le nom de l'interface de base, avec
mutablerectangle code> une spécialisation, et peut-être
immutablérectangle code> une autre spécialisation, avec une fonction pour "geler" un rectangle.
@Steve Jessop - Oui, mais le nom implique une immuabilité comme invariant de classe. Le problème est que l'assurance-mutabilité est un trait viral - une seule opération de mutation et l'objet est mutable, quel que soit le nombre d'opérations non mutatings, il peut y avoir. Je suis revenu pour dire que et fondamentalement "Qu'en est-il de rectangle Isa Rectanglebasebasebase?", Mais "rectangle a construisible" est bon. La clarification de l'immuabilité n'est pas une invariative de classe, mais la charge utile non mutation est - cela fait une différence. OTOH, l'anglais n'a aucune obligation de fournir à La Mot Juste.
@Steve Jessop: Les objets immuables ont un contrat avec tout utilisateur prometteur qu'ils ne changeront pas. Deux objets immuables comparateurs égaux comparent toujours égaux et peuvent être utilisés de manière interchangeable. Je suggérerais d'avoir une classe de base générique avec des propriétés pour lire des attributs de forme; Il peut reprocher des descendants mutables et immuables; Des choses qui ne changent pas de forme elles-mêmes mais ne se soucient pas si quelqu'un d'autre pourrait le faire peut accepter des objets de la classe générique. Les classes immuables risquent de bien accepter les objets de classe générique dans leurs constructeurs de copies.
Je crois, bien que je ne suis pas certain, que vous devez utiliser une extinction explicite pour invoquer cet opérateur de conversion dans ce contexte. La base code> immutablérectangle code> est une solution commune et efficace. De même, vous pouvez utiliser une solution plus abstraite telle que:
Eh bien, je suis surpris. Il semble hériter de manière privée d'une classe A empêche d'utiliser l'opérateur A à l'extérieur de la classe.
Vous pouvez résoudre votre problème en faisant un rectangle de membre pour le carré et en l'utilisant pour la distribution: P>
class Square { Rectangle r; public: operator const Rectangle&() const { return r; } };
Cela a bien fonctionné pour moi (il suffit d'ajouter "R." devant quelques choses). Bien que je ne comprends pas pourquoi l'héritage privé affecte l'interface publique d'une classe. Je pensais que privé signifiait privé.