Comment déclarer une interface en C++?
Comment puis-je configurer une classe qui représente une interface? Est-ce juste une classe de base abstraite?
Vous devez vous connecter pour publier un commentaire.
Comment puis-je configurer une classe qui représente une interface? Est-ce juste une classe de base abstraite?
Vous devez vous connecter pour publier un commentaire.
Pour étoffer la réponse par bradtgmurray, vous pouvez faire une exception à la méthode virtuelle pure liste de votre interface en ajoutant un destructeur virtuel. Cela vous permet de passer le pointeur de la propriété à une autre partie sans exposer le béton de la classe dérivée. Le destructeur n'a pas à faire quoi que ce soit, parce que l'interface n'ont pas de structures en béton. Il peut sembler contradictoire de définir une fonction à la fois virtuel et en ligne, mais croyez-moi - il n'est pas.
Vous n'avez pas à inclure un corps pour le destructeur virtuel -, il s'avère que certains compilateurs ont de la difficulté de l'optimisation d'un vide destructeur et il est préférable d'utiliser la valeur par défaut.
=0
) destructeur avec un corps. L'avantage ici est que le compilateur peut, théoriquement, de voir que vtable n'a pas de membres valides maintenant, et l'écarter complètement. Avec un destructeur virtuel avec un corps, dit destructeur peut être appelé (virtuellement) par exemple, dans le milieu de la construction parthis
pointeur (lorsque l'objet construit est toujours deParent
type), et donc que le compilateur doit fournir une preuve de vtable. Donc, si vous n'avez pas explicitement appeler les destructeurs virtuels viathis
au cours de la construction 🙂 vous pouvez enregistrer de la taille du code.throw
du constructeur d'une classe dérivée.Base
<-Derived
<-MostDerived
. Vous êtes dans la Dérivée ctor. Vous appelez une fonction globale, qui prend unBase*
argument, en passant ilthis
. Cette fonction peut appeler explicitement le destructeur via le pointeur, et il doit être distribué pratiquement.MostDerived
constructeur à être très malheureux. Au moins avec les détruire viathrow
le reste de la construction est court-circuité.MostDerived
, mais je ne vois pas ce qui ferait par exemplethis->~Foo()
dans un ctor UB tous par lui-même. Considérons le cas où vous détruire, et de ne jamais revenir à la ctor (par exemple, juste assis là dans une boucle infinie, de prise et de traitement de l'input). À l'intérieur de cette boucle, votre comportement serait bien définis, autant que je peux voir.override
mot-clé pour permettre la compilation de l'argument et la valeur de retour de vérification de type. Par exemple, dans la déclaration de l'Enfantvirtual void OverrideMe() override;
*a = *b
mais je vous l'accorde il n'est pas commun.Child
, est-il encore nécessaire d'écrireOverrideMe()
commevirtual void
?void
est toujours nécessaire.Faire une classe avec des méthodes virtuelles pures. Utilisation de l'interface par la création d'une autre classe qui remplace ces méthodes virtuelles.
Une méthode virtuelle pure est une méthode de classe est défini comme virtuel et affecté à 0.
override
en C++11Child::OverrideMe()
commevirtual
, @Cemre, parce queIDemo::OverrideMe()
faitChild::OverrideMe()
implicitementvirtual
. Avoir une indication que c'est une fonction virtuelle (commevirtual
, ou mieux encore,override
en C++11 ou une version ultérieure) servira comme un rappel pour les programmeurs, avecoverride
en particulier aussi aider le compilateur de vérifier qu'il ne fait écraser quelque chose.La raison pour laquelle vous disposez d'une Interface spéciale de type catégorie en plus des classes de base abstraites en C#/Java est parce que C#/Java ne supporte pas l'héritage multiple.
C++ supporte l'héritage multiple, et donc un type spécial n'est pas nécessaire. Une classe de base abstraite avec des non-abstrait (virtuelle pure) méthodes est fonctionnellement équivalent à un C#/Java de l'interface.
java.lang.Thread
classe etjava.lang.Runnable
de l'interface, qui n'existe tout simplement parce que vous ne pouvez pas étendreThread
avec une autre base. De la documentation, il peut sembler qu'il est fourni pour votre choix, mais une fois j'ai été forcé d'utiliserRunnable
en raison de l'absence d'héritage multiple.Thread
instance. L'héritage Multiple peut être une mauvaise conception ainsi que la composition. Tout dépend des cas.abstract
mot-clé en C++.Il n'y a pas de notion de "interface" en soi en C++. Autant que je sache, les interfaces ont été introduites dans Java pour contourner l'absence d'héritage multiple. Ce concept s'est avéré être tout à fait 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 l'un des membres de la fonction (méthode en Java le jargon) est une fonction virtuelle pure déclarées à l'aide de la syntaxe suivante:
Une classe de base abstraite ne peut pas être instanciée, j'. e. vous ne pouvez pas déclarer un objet de la classe A. Vous ne pouvez constituer des classes à partir d'Un, mais de toute classe dérivée qui n'est pas de fournir une implémentation de
foo()
sera aussi abstrait. Afin de cesser d'être abstraite, une classe dérivée doit fournir des implémentations pour toutes les fonctions virtuelles pures il hérite.Noter qu'une classe de base abstraite peut être plus qu'une interface, car il peut contenir des données des membres et des fonctions membres qui ne sont pas du pur virtuel. L'équivalent d'une interface serait une classe de base abstraite, sans données avec seulement des fonctions virtuelles pures.
Et, en tant que Marque de Rançon a souligné, une classe de base abstraite doit fournir un destructeur virtuel, comme n'importe quelle classe de base, pour cette question.
class Button; class Image; class ClickableIcon : public Button, public Image {};
. S'ils sont correctement conçus, Image offre les fonctionnalités graphiques et Bouton d'alimentation du fonctionnement interactif, sans chevauchement qui pourraient causer des problèmes, tandis que ClickableIcon lui-même gère la fonctionnalité de la touche est pour.Button
fournit interactive code,Image
fournit le code des graphismes, etClickableIcon
fournit un code spécifique à lui-même, ainsi que tout le code nécessaire pour souder les deux classes parents ensemble. Aussi longtemps que les deux classes sont bien conçus, et n'ont pas de chevauchement des noms de membres de la classe devrait être entièrement viable, et plus facile à mettre en œuvre que si l'un des deux était une interface.Autant j'ai pu tester, il est très important d'ajouter le destructeur virtuel. Je suis en utilisant les objets créés avec
new
et détruit avecdelete
.Si vous n'ajoutez pas le destructeur virtuel dans l'interface, puis 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ée.Ma réponse est essentiellement la même que les autres, mais je pense qu'il y a deux autres choses importantes à faire:
Déclarer un destructeur virtuel dans votre interface ou faire un protégé non virtuelle d'éviter l'indéfini comportements si quelqu'un essaie de supprimer un objet de type
IDemo
.Utiliser l'héritage virtuel pour éviter les problèmes avec l'héritage multiple. (Il n'y a plus souvent l'héritage multiple lorsque nous utilisons des interfaces.)
Et comme d'autres réponses:
Utilisation de l'interface par la création d'une autre classe qui remplace ces méthodes virtuelles.
Ou
Et
En C++11, vous pouvez facilement éviter l'héritage tout à fait:
Dans ce cas, une Interface de référence sémantique, c'est à dire que vous avez à faire assurez-vous que l'objet survit à l'interface (il est également possible de faire des interfaces avec la valeur sémantique).
Ce type d'interfaces ont leurs avantages et leurs inconvénients:
Enfin, l'héritage est la racine de tous les maux dans la conception de logiciels complexes. Dans Sean Parent de la Valeur de la Sémantique et de Concepts basés sur le Polymorphisme (fortement recommandé, les meilleures versions de cette technique y sont expliqués) le cas suivant est étudié:
Dire que j'ai une application dans laquelle je traite avec mes formes polymorphically à l'aide de la
MyShape
interface:Dans votre application, vous faites la même chose avec des formes différentes à l'aide de la
YourShape
interface:Disent maintenant que vous souhaitez utiliser certaines des formes que j'ai développé dans votre application. Sur le plan conceptuel, nos formes ont la même interface, mais pour faire mes formes de travail dans votre application, vous devrez étendre mes formes comme suit:
Tout d'abord, la modification de mes formes pourrait ne pas être possible à tous. En outre, l'héritage multiple conduit la route de code spaghetti (imaginez un troisième projet en utilisant le
TheirShape
interface... ce qui se passe si ils demandent également de leur fonction drawmy_draw
?).Mise à jour: Il y a un couple de nouvelles références sur le non-héritage polymorphisme basé sur:
Circle
classe est une mauvaise conception. Vous devez utiliserAdapter
modèle dans de tels cas. Désolé si ça va paraître un peu dur, mais essayez d'utiliser certains de la vie réelle de la bibliothèque commeQt
avant de faire des jugements à propos de 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à? Prescience? C'est pourquoi il est détaché de la réalité. Et en réalité, si vous choisissez de compter sur des "MyShape" bibliothèque vous pouvez adopter pour son interface depuis le début. Dans les formes exemple il y a beaucoup de bêtises (dont l'une est que vous avez deuxCircle
les structures), mais l'adaptateur ressemblerait à quelque chose comme ça -> ideone.com/UogjWkAdapter
modèle. Fournir la mise en œuvre de l'interface comme dans votre réponse signifie que je dois fournir toutes les méthodes virtuelles, qui, autrement, pourrait être tout simplement hérité.object_t
internes, ni je ne peux pas hériter d'après elle? De ce que je vois, je dois mettre en œuvredraw()
fonction à partir de zéro sur mon propre et je ne suis pas lié àconcept_t
interface. Aussi, avec des méthodes virtuelles, je peux avoir des implémentations par défaut, ce que je peux remplacer ou non.gnzlbg::vector
dans votre classe et je fournir interface avec mes personnalisédoc::vector
et vous devez convertir les données d'entrée et de sortie à votre intérieur. Où est notre code de la ré-utilisation et efficacité si vous avez besoin de les convertir?doc::vector
ougnzlbg::vector
sur l'interface local. Vous devez apprendre à bien utiliser l'héritage, parce que ce que vous dites maintenant, n'a aucun sens!Toutes les bonnes réponses ci-dessus.
Une autre petite chose que vous devez garder à l'esprit que vous pouvez également avoir un destructeur virtuel pur. La seule différence est que vous avez encore besoin de la mettre en œuvre.
Confus?
La principale raison pour laquelle vous voulez faire si vous souhaitez fournir des méthodes d'interface, comme je l'ai, mais de faire de leur remplaçant, en option.
À faire de la classe une classe d'interface nécessite une méthode virtuelle pure, mais l'ensemble de vos méthodes virtuelles ont des implémentations par défaut, de sorte que la seule méthode à gauche pour faire virtuelle pure est le destructeur.
De réimplanter un destructeur de la classe dérivée est pas une grosse affaire à tous - j'ai toujours réimplémenter un destructeur virtuel ou pas, dans mes classes dérivées.
Si vous utilisez Microsoft C++ compilateur, vous pouvez effectuer les opérations suivantes:
J'aime cette approche, car il en résulte un beaucoup plus petit code de l'interface et de la taille du code généré peut être considérablement réduite. L'utilisation de novtable supprime toute référence à la vtable pointeur de la classe, de sorte que vous pouvez ne jamais l'instancier directement. Voir la documentation ici - novtable.
novtable
sur la norme devirtual void Bar() = 0;
= 0;
que j'ai ajouté). Lire la documentation si vous ne le comprenez pas.= 0;
et assumé, c'est juste un non-standard de la façon de faire exactement la même chose.Un peu plus de ce qui est écrit là-haut:
Tout d'abord, assurez-vous que votre destructeur est aussi virtuelle pure
Seconde, vous pouvez hériter virtuellement (plutôt que normalement) lorsque vous procédez à mettre en œuvre, pour faire bonne mesure.
class Base { public: ~Base() = 0; }; Base::~Base() {}
, pour permettre au compilateur de générer des constructeurs par défaut pour les classes dérivéesVous pouvez également envisager de contrat de classes mises en œuvre avec l'INB (Non Virtuel de l'Interface de Modèle). Par exemple:
Je suis encore nouveau dans le développement en C++. J'ai commencé avec Visual Studio (VS).
Pourtant, personne ne semble mentionné le
__interface
VS (.NET). Je suis pas très sûr si c'est une bonne façon de déclarer une interface. Mais il semble fournir un d'application supplémentaires (mentionné dans les documents). De telle sorte que vous n'avez pas à spécifier explicitement levirtual TYPE Method() = 0;
, car il sera automatiquement converti.Si quelqu'un n'ai rien d'intéressant à ce sujet, s'il vous plaît partager. 🙂
Grâce.
Même s'il est vrai que
virtual
est le standard de facto pour définir une interface, il ne faut pas oublier le classique C-comme le modèle, qui est livré avec un constructeur en C++:Ceci a l'avantage que vous pouvez re-lier les événements d'exécution, sans avoir à construire votre classe à nouveau (comme le C++ n'a pas une syntaxe permettant de modifier des types polymorphes, c'est une solution de contournement pour caméléon classes).
Conseils:
click
dans votre descendant du constructeur.protected
membre et d'avoir unpublic
de référence et/ou de lecture.if
s vs modifications de l'état dans votre code, ce pourrait être plus rapide queswitch()
es ouif
s (redressement est attendu autour de 3-4if
s, mais toujours mesurer en premier.std::function<>
sur des pointeurs de fonction, vous pourrait être en mesure de gérer l'ensemble de vos données d'objet dansIBase
. À partir de ce point, vous pouvez avoir de la valeur de schémas pourIBase
(par exemple,std::vector<IBase>
de travail). Notez que cette pourrait être inférieure en fonction de votre compilateur et le code STL, et aussi que les implémentations actuelles destd::function<>
ont tendance à avoir une surcharge par rapport à des pointeurs de fonction ou même des fonctions virtuelles (cela pourrait changer dans le futur).Voici la définition de
abstract class
en c++ standardn4687
13.4.2
Résultat:
Zone du Rectangle: 35
L'aire du Triangle: 17
Nous avons vu comment une classe abstraite définie une interface en termes de getArea() et les deux autres classes de mise en œuvre de la même fonction mais avec un autre algorithme pour calculer la surface spécifique de la forme.