Comment configurer une classe qui représente une interface? Est-ce juste une classe de base abstraite?
c++
inheritance
interface
abstract-class
pure-virtual
Aaron Fischer
la source
la source
Réponses:
Pour développer la réponse de bradtgmurray , vous souhaiterez peut-être faire une exception à la liste des méthodes virtuelles pures de votre interface en ajoutant un destructeur virtuel. Cela vous permet de transmettre la propriété du pointeur à une autre partie sans exposer la classe dérivée concrète. Le destructeur n'a rien à faire, car l'interface n'a pas de membres concrets. Il peut sembler contradictoire de définir une fonction à la fois virtuelle et en ligne, mais croyez-moi, ce n'est pas le cas.
Vous n'avez pas besoin d'inclure un corps pour le destructeur virtuel - il s'avère que certains compilateurs ont du mal à optimiser un destructeur vide et il vaut mieux utiliser la valeur par défaut.
la source
=0
destructeur virtuel ( ) pur avec un corps. L'avantage ici est que le compilateur peut, théoriquement, voir que vtable n'a plus de membres valides maintenant, et le supprimer complètement. Avec un destructeur virtuel avec un corps, ledit destructeur peut être appelé (virtuellement) par exemple au milieu de la construction via unthis
pointeur (lorsque l'objet construit est toujours deParent
type), et donc le compilateur doit fournir une vtable valide. Donc, si vous n'appelez pas explicitement les destructeurs virtuels viathis
pendant la construction :), vous pouvez économiser sur la taille du code.override
mot clé pour permettre l'argument de compilation et la vérification du type de valeur de retour. Par exemple, dans la déclaration de Childvirtual void OverrideMe() override;
Créez une classe avec des méthodes virtuelles pures. Utilisez l'interface en créant une autre classe qui remplace ces méthodes virtuelles.
Une méthode virtuelle pure est une méthode de classe définie comme virtuelle et affectée à 0.
la source
override
en C ++ 11La raison pour laquelle vous avez une catégorie de type Interface spéciale en plus des classes de base abstraites en C # / Java est parce que C # / Java ne prend pas en charge l'héritage multiple.
C ++ prend en charge l'héritage multiple, et donc un type spécial n'est pas nécessaire. Une classe de base abstraite sans méthode non abstraite (virtuelle pure) est fonctionnellement équivalente à une interface C # / Java.
la source
Thread
instance. L'héritage multiple peut être une mauvaise conception ainsi qu'une mauvaise composition. Tout dépend du cas.Il n'y a pas de concept d '"interface" en soi en C ++. AFAIK, les interfaces ont d'abord été introduites en Java pour contourner le manque d'héritage multiple. Ce concept s'est avéré très utile et le même effet peut être obtenu en C ++ en utilisant une classe de base abstraite.
Une classe de base abstraite est une classe dans laquelle au moins une fonction membre (méthode en langage Java) est une fonction virtuelle pure déclarée à l'aide de la syntaxe suivante:
Une classe de base abstraite ne peut pas être instanciée, c'est-à-dire que vous ne pouvez pas déclarer un objet de classe A. Vous pouvez uniquement dériver des classes de A, mais toute classe dérivée qui ne fournit pas d'implémentation de
foo()
sera également abstraite. Pour ne plus être abstraite, une classe dérivée doit fournir des implémentations pour toutes les fonctions virtuelles pures dont elle hérite.Notez qu'une classe de base abstraite peut être plus qu'une interface, car elle peut contenir des membres de données et des fonctions membres qui ne sont pas purement virtuels. Un équivalent d'une interface serait une classe de base abstraite sans aucune donnée avec uniquement des fonctions virtuelles pures.
Et, comme l'a souligné Mark Ransom, une classe de base abstraite devrait fournir un destructeur virtuel, comme toute classe de base, d'ailleurs.
la source
Autant que j'ai pu tester, il est très important d'ajouter le destructeur virtuel. J'utilise des objets créés avec
new
et détruits avecdelete
.Si vous n'ajoutez pas le destructeur virtuel dans l'interface, le destructeur de la classe héritée n'est pas appelé.
Si vous exécutez le code précédent sans
virtual ~IBase() {};
, vous verrez que le destructeurTester::~Tester()
n'est jamais appelé.la source
Ma réponse est essentiellement la même que les autres, mais je pense qu'il y a deux autres choses importantes à faire:
Déclarez un destructeur virtuel dans votre interface ou créez-en un non virtuel pour éviter les comportements non définis si quelqu'un essaie de supprimer un objet de type
IDemo
.Utilisez l'héritage virtuel pour éviter les problèmes d'héritage multiple. (Il y a plus souvent un héritage multiple lorsque nous utilisons des interfaces.)
Et comme les autres réponses:
Utilisez l'interface en créant une autre classe qui remplace ces méthodes virtuelles.
Ou
Et
la source
Dans C ++ 11, vous pouvez facilement éviter complètement l'héritage:
Dans ce cas, une interface a une sémantique de référence, c'est-à-dire que vous devez vous assurer que l'objet survit à l'interface (il est également possible de faire des interfaces avec une sémantique de valeur).
Ces types d'interfaces ont leurs avantages et leurs inconvénients:
Enfin, l'héritage est la racine de tout mal dans la conception de logiciels complexes. Dans Sean Parent's Value Semantics and Concepts-based Polymorphism (fortement recommandé, de meilleures versions de cette technique y sont expliquées), le cas suivant est étudié:
Disons que j'ai une application dans laquelle je traite mes formes de manière polymorphe en utilisant l'
MyShape
interface:Dans votre application, vous faites de même avec différentes formes en utilisant l'
YourShape
interface:Supposons maintenant que vous souhaitiez utiliser certaines des formes que j'ai développées dans votre application. Conceptuellement, nos formes ont la même interface, mais pour que mes formes fonctionnent dans votre application, vous devez étendre mes formes comme suit:
Tout d'abord, la modification de mes formes pourrait ne pas être possible du tout. De plus, l'héritage multiple ouvre la voie au code spaghetti (imaginez qu'un troisième projet arrive qui utilise l'
TheirShape
interface ... que se passe-t-il s'ils appellent également leur fonction de dessinmy_draw
?).Mise à jour: Il existe quelques nouvelles références sur le polymorphisme non hérité:
la source
Circle
classe est une mauvaise conception. Vous devez utiliser leAdapter
modèle dans de tels cas. Désolé si cela peut sembler un peu dur, mais essayez d'utiliser une bibliothèque réelle commeQt
avant de porter un jugement sur l'héritage. L'héritage rend la vie beaucoup plus facile.Adapter
modèle? Je suis intéressé de voir ses avantages.Square
n'est pas déjà là? Connaissance anticipée? C'est pourquoi il est détaché de la réalité. Et en réalité, si vous choisissez de vous appuyer sur la bibliothèque "MyShape", vous pouvez l'adopter dès le début. Dans l'exemple de formes, il y a beaucoup de non-sens (dont l'un est que vous avez deuxCircle
structures), mais l'adaptateur ressemblerait à quelque chose comme ça -> ideone.com/UogjWkToutes les bonnes réponses ci-dessus. Une chose supplémentaire que vous devez garder à l'esprit - vous pouvez également avoir un destructeur virtuel pur. La seule différence est que vous devez toujours l'implémenter.
Confus?
La principale raison pour laquelle vous voudriez le faire est que si vous souhaitez fournir des méthodes d'interface, comme je l'ai fait, mais que vous les rendiez facultatives.
Pour faire de la classe une classe d'interface, il faut une méthode virtuelle pure, mais toutes vos méthodes virtuelles ont des implémentations par défaut, donc la seule méthode qui reste pour faire du virtuel pur est le destructeur.
Réimplémenter un destructeur dans la classe dérivée n'est pas un problème du tout - je réimplémente toujours un destructeur, virtuel ou non, dans mes classes dérivées.
la source
Si vous utilisez le compilateur C ++ de Microsoft, vous pouvez effectuer les opérations suivantes:
J'aime cette approche car elle se traduit par un code d'interface beaucoup plus petit et la taille du code généré peut être considérablement plus petite. L'utilisation de novtable supprime toute référence au pointeur vtable dans cette classe, vous ne pouvez donc jamais l'instancier directement. Voir la documentation ici - novtable .
la source
novtable
plus que la normevirtual void Bar() = 0;
= 0;
que j'ai ajouté). Lisez la documentation si vous ne la comprenez pas.= 0;
et j'ai supposé que c'était juste une façon non standard de faire exactement la même chose.Un petit ajout à ce qui est écrit là-bas:
Tout d'abord, assurez-vous que votre destructeur est également purement virtuel
Deuxièmement, vous souhaiterez peut-être hériter virtuellement (plutôt que normalement) lorsque vous l'implémenterez, juste pour de bonnes mesures.
la source
Vous pouvez également envisager des classes de contrat implémentées avec le NVI (Non Virtual Interface Pattern). Par exemple:
la source
Je suis encore nouveau dans le développement C ++. J'ai commencé avec Visual Studio (VS).
Pourtant, personne ne semble avoir mentionné le
__interface
dans VS (.NET) . Je ne suis pas très sûr que ce soit un bon moyen de déclarer une interface. Mais il semble fournir une application supplémentaire (mentionnée dans les documents ). Telle que vous n'avez pas à spécifier explicitement levirtual TYPE Method() = 0;
, car il sera automatiquement converti.Si quelqu'un a quelque chose d'intéressant à ce sujet, veuillez le partager. :-)
Merci.
la source
S'il est vrai que
virtual
c'est la norme de facto pour définir une interface, n'oublions pas le modèle classique de type C, qui vient avec un constructeur en C ++:Cela a l'avantage que vous pouvez lier à nouveau le runtime des événements sans avoir à reconstruire votre classe (car C ++ n'a pas de syntaxe pour changer les types polymorphes, c'est une solution de contournement pour les classes caméléons).
Conseils:
click
le constructeur de votre descendant.protected
membre et avoir unepublic
référence et / ou un getter.if
changements de s par rapport à l'état dans votre code, cela peut être plus rapide queswitch()
es ouif
s (un délai est prévu autour de 3-4if
s, mais mesurez toujours en premier.std::function<>
des pointeurs de fonction, vous pourrez peut- être gérer toutes vos données d'objet à l'intérieurIBase
. À partir de ce point, vous pouvez avoir des schémas de valeurIBase
(par exemple,std::vector<IBase>
cela fonctionnera). Notez que cela peut être plus lent selon votre compilateur et votre code STL; également que les implémentations actuelles destd::function<>
ont tendance à avoir un surcoût par rapport aux pointeurs de fonction ou même aux fonctions virtuelles (cela pourrait changer à l'avenir).la source
Voici la définition de la
abstract class
norme c ++n4687
13.4.2
la source
Résultat: Rectangle: 35 Triangle: 17
Nous avons vu comment une classe abstraite a défini une interface en termes de getArea () et deux autres classes ont implémenté la même fonction mais avec un algorithme différent pour calculer la zone spécifique à la forme.
la source