Giles314
Messages postés21Date d'inscriptionsamedi 23 décembre 2006StatutMembreDernière intervention23 avril 2021
-
20 déc. 2007 à 11:25
Giles314
Messages postés21Date d'inscriptionsamedi 23 décembre 2006StatutMembreDernière intervention23 avril 2021
-
23 déc. 2007 à 02:11
Bonjour,
Ma question est quelque peu théorique car j'ai une solution mais qui ne me semble pas "pure".
Ce que j'appelle une interface est une spécification sans code associé (virtuelle pure) par exemple
class Interface
{
virtual int Run (void) = 0;
virtual ~Interface () = 0;
};
Malheureusement une classe comme celle-ci ne se linke pas dans mon environnement gcc sous MinGW (mais je suppose que c'est vrai avec d'autres compilateurs).
En effet si je déclare:
class Implementation : public Interface
{
Implementation();
virtual int Run (void);
virtual ~Implementation ();
};
int main()
{
Implementation Programme;
return Programme.Run();
}
Toute utilisation de Implementation va générer une référence à la fonction Interface::~Interface qui n'existe pas.
Y a-t-il une solution à ce problème sans définir le destructeur (virtual ~Interface (){}) qui mettrait du code dans l'interface - certe pas beaucoup - qui perdrait ainsi son status d'interface pure ?
Je sais je suis un peu tatillon, mais une interface c'est juste une interface
Giles314
Messages postés21Date d'inscriptionsamedi 23 décembre 2006StatutMembreDernière intervention23 avril 2021 20 déc. 2007 à 18:42
Effectivement il manque les public:
Il faut considérer que toutes les déclarations de l'interface et de l'implementation le sont. Là n'est pas le problème.
Par contre il me faut effectivement obligatoirement un destructeur virtuel. Sinon si j'utilise mon implémentation comme décrit ci-dessous le destructeur de l'implémentation ne sera pas appelé. Il ne faut jamais interdire lors de la définition d'une interface que l'implémentation ait des destructeurs virtuels sinon on restreint considérablement soit les implémentations (pas de destructeur) soit les utilisations (pas d'utilisation polymorphique ce qui réduit considérablement l'intéret d'une interface!).
int main()
{
Implementation *Programme = new Programme;
int Result = Programme.Run();
delete Programme;
return Result;
}
cs_juju12
Messages postés966Date d'inscriptionsamedi 3 avril 2004StatutMembreDernière intervention 4 mars 20104 20 déc. 2007 à 20:36
Bon...mais pourquoi ne veux-tu pas implémenter le destructeur de Interface? Tu peux le mettre en inline avec rien dedans par exemple :
class Interface
{
virtual int Run (void) = 0;
virtual ~Interface () = 0 {return;};
};
de cette manière tu gardes le destructeur comme fonction virtuelle pure, mais il est tout de même implémenté pour l'interface; de toutes façons le compilateur virera l'appel puisqu'il ne fait rien.
Vous n’avez pas trouvé la réponse que vous recherchez ?
luhtor
Messages postés2023Date d'inscriptionmardi 24 septembre 2002StatutMembreDernière intervention28 juillet 20086 20 déc. 2007 à 20:53
Oui en effet, au temps pour moi, pour le coup du destructeur. J'essai de voir, car je suis intrigué par ce problème. J'ai essayé sous devcpp, j'ai le meme soucis.
Bon, mais j'ai cherché autour de moi, la réponse, c'est qu'apparemment, on ne peut pas faire ce que tu souhaites avec le C++.
Donc le problème vient de ta conception. Tu ne peux pas gérer une interface comme un objet et donc on ne peut pas détruire une interface.
Donc l'objet Implementation doit être détruit par celui qui a créé ton objet.
luhtor
Messages postés2023Date d'inscriptionmardi 24 septembre 2002StatutMembreDernière intervention28 juillet 20086 20 déc. 2007 à 20:57
Non Juju12, car dans ton cas, la classe Interface n'est plus une interface et chaque objet de type Interface* possède une vtable, et donc Giles314 aura de gros ennui plus tard s'il commence a faire de l'héritage multiple à cause de l'héritage multiple. Sauf si dans la structure du programme de Giles314, l'héritage multiple ne présente pas de pb.
Giles314
Messages postés21Date d'inscriptionsamedi 23 décembre 2006StatutMembreDernière intervention23 avril 2021 21 déc. 2007 à 00:32
Je ne vois pas bien à quoi l'héritage multiple pourrait être une solution au problème.
J'ai un peu peur que Luthor ait raison et qu'on ne puisse par faire une interface pure en C++. C'est aussi vers cette conclusion que mes essais m'avaient conduit. Mais j'ai du mal à m'y résoudre car le concept d'interface est quand même un pilier de la conception objet et je n'imagine pas que les travaux de normalisation du C++ n'ait pas ouvert la possibilité de créer une interface pure. Mais il est vrai qu'en autorisant l'implémentation d'une méthode virtuelle pure le C++ permet une ineptie totalement inutile mais qui semble interdire d'implémenter proprement un concept de base.
Sinon la proposition de Juju12 est correcte et correspond à la façon dont j'approche aujourd'hui au plus près le concept d'interface (sauf que perso je ne mets même pas le return dans le destructeur car les accolades suffisent). Si bien qu'on obtient ce que Luthor dit ne pas être possible (et effectivement j'aimerai ne pas le faire) c'est à dire "gérer une interface comme un objet" et fournir une méthode pour "détruire une interface."
luhtor
Messages postés2023Date d'inscriptionmardi 24 septembre 2002StatutMembreDernière intervention28 juillet 20086 21 déc. 2007 à 08:20
"C++ permet une ineptie totalement inutile mais qui semble interdire d'implémenter proprement un concept de base."
=> Il ne l'interdit pas. Mais une interface n'est pas faite pour etre détruite. Elle ne décrit qu'un aspect fonctionnel de l'objet et elle ne peut être considérée comme l'objet lui meme. Donc ce n'est pas le C++ qui interdit de le faire, mais la théorie qui dirait que : détruire une interface n'a pas de sens et ne résulte que d'une mauvaise conception.
Giles314
Messages postés21Date d'inscriptionsamedi 23 décembre 2006StatutMembreDernière intervention23 avril 2021 21 déc. 2007 à 10:48
"détruire une interface n'a pas de sens et ne résulte que d'une mauvaise conception." Je suis tout à fait d'accord.
C'est pourquoi je demande comment définir une interface en C++ sans donner d'implémentation à son destructeur. C'est exactement l'objet de ma question initiale qui ne semble malheureusement pas avoir de réponse. Il semble donc que la "mauvaise conception" est intrinsèque au langage.
Mais j'ai toujours espoir (de moins en moins en fait) qu'une astuce méconnue comme le C++ en a quelques unes (du genre de l'héritage virtuel ou l'implémentation des méthodes virtuelles pures qui ont été évoqués précédemment, ou encore la surcharge des opérateurs postfixés, les new sans allocation de mémoire,... ) permette de résoudre le problème avec un peu plus d'élégance.
cs_juju12
Messages postés966Date d'inscriptionsamedi 3 avril 2004StatutMembreDernière intervention 4 mars 20104 21 déc. 2007 à 15:54
Bon en fait sous Visual Studio y a un mot-clé spécial pour faire ça : __interface (s'utilise à la place de class)
J'ai bien lu que tu n'utilisais pas ce compilateur, simplement ça confirme ce que tu penses, à savoir qu'il y a des chances qu'on ne puisse pas le faire autrement sinon ce mot-clé n'aurait pas de raison d'être...; peut-être des équivalents sous d'autres compilos...
Giles314
Messages postés21Date d'inscriptionsamedi 23 décembre 2006StatutMembreDernière intervention23 avril 2021 21 déc. 2007 à 17:04
Merci. Je pense effectivement que par inférence ça répond négativement à la question concernant la norme C++, et cela fournit une réponse (que je n'ai pas vérifiée) pour les extensions VC++.
Par contre après avoir revu tous les attributs, pragmas et extentions gérés par Gcc je n'ai rien trouvé d'équivalent.
Il y a bien un pragma interface pour demander de ne pas générer le code mais cela ne pourra qu'empirer le problème du link.
cs_juju12
Messages postés966Date d'inscriptionsamedi 3 avril 2004StatutMembreDernière intervention 4 mars 20104 21 déc. 2007 à 17:52
Mais comment fais-tu après lutor? Si j'ai bien compris Giles veut utiliser l'interface pour donner des méthodes à un objet donc par héritage; mais dans ce cas l'objet aura besoin de constructeur/destructeur parents sinon ça ne compile pas évidemment...
Giles314
Messages postés21Date d'inscriptionsamedi 23 décembre 2006StatutMembreDernière intervention23 avril 2021 21 déc. 2007 à 18:28
Si ça compile très bien. C'est au link que ça déconne car il cherche l'implémentation de
class Interface
{
virtual int Run() = 0;
};
En effet quelqu'un, qui aurait mieux fait de se recoucher se jour là, a trouvé super puissant d'autoriser d'implémenter les méthodes virtuelles pures en C++. Donc le destructeur des classes dérivées appelle ce destructeur, bien que virtuel pur, au cas où il existerait... Et s'il existe pas ça fait une référence indéfinie au link.
bizibiz17
Messages postés142Date d'inscriptionmardi 17 janvier 2006StatutMembreDernière intervention29 août 20091 21 déc. 2007 à 20:54
Salut,
D'après ce que je connais sur les interfaces il s'agit de classes virtuelles c'est-à-dire que tu ne peut pas les utiliser comme des objets tels quels, il faut implémenter les méthodes dans des classes qui héritent de l'interface.
exemple :
class Implementation : public Interface
{
int methode_test();
};
class test : public Implementation
{
//C'est ici qu'est implémentée la méthode "methode_test" de l'interface
int methode_test()
{
return 0;
}
};
int main()
{
Implementation Programme = new test();
Programme.methode_test();
}
Voilà je suis par sûr de ce que je dis mais en tout cas en Java c'est comme ça que ça marche et en c++ aussi me semble-t-il...
nickydaquick
Messages postés416Date d'inscriptionvendredi 31 janvier 2003StatutMembreDernière intervention19 décembre 20133 21 déc. 2007 à 22:22
Salut,
pour une interface (pratique pour definir une API), le concept est simple:
1- definir une classe abstraite avec un destructeur virtuel(tres important), toutes les fonctions sont donc virtuelles pures (pour redefinir les fonctions plus tard)
2- definir une derivation virtuelle (avec les destructeurs des classes derivees virtuels eux aussi , tres important)
exemple:
class Interface
{
public:
explicit Interface(){};
virtual ~Interface(){};
virtual void methode()const = 0;
};
class Implementation: virtual public Interface
{
public:
Implementation():Interface(){};
virtual ~Implementation(){};
virtual void methode()const{};
};
Fonctionne avec Visual Studio 6.0 Professionnel (Windows XP) et KDevelop 2 (Linux Fedora Core)
J'espere avoir aide, salut
je suis heureux de faire partie d'une grande famille ...!
luhtor
Messages postés2023Date d'inscriptionmardi 24 septembre 2002StatutMembreDernière intervention28 juillet 20086 21 déc. 2007 à 23:51
Non je le dis et répète, on ne met pas de destructeur dans une interface. Par défaut, en l'absence de destructeur dans une classe, le compilo en génèrera un si besoin. Dans le cas d'une interface, aucun besoin de constructeur/destructeur. Dans le cas général, une classe qui n'a pas de destructeur de pose aucun problème tant que l'on fait pas d'alloc dynamiques.
[auteur/NICKYDAQUICK/43984.aspx nickydaquick]: "1-
definir une classe abstraite avec un destructeur virtuel(tres
important), toutes les fonctions sont donc virtuelles pures (pour
redefinir les fonctions plus tard)"
=> Non et non :) Une interface n'est pas une classe, ca n'a rien a voir. Donc: pas de destructeur pour une interface. De toute facon, ca sert a rien: il n'y a rien a détruire dans une interface. Et comme on ne détruit jamais une interface, aucun besoin de déclarer un destructeur virtuel.
Giles314
Messages postés21Date d'inscriptionsamedi 23 décembre 2006StatutMembreDernière intervention23 avril 2021 22 déc. 2007 à 01:04
Luthor, il semble vraiment que l'utilité d'une interface t'a échappée. Il permet de connaître les caractéristiques dont on a à connaître (dans l'exemple l'objet a une fonction Run ou methode et un destructeur) sans connaître l'implémentation qui par ailleurs peut être multiple.
Comme le but de l'exemple n'était pas de montrer à quoi sert une interface mais à demander comment on peut la créer, mon exemple était un peu simpliste et l'intéret de l'interface pas flagrant. Mais complexifions un peu cet exemple en créant 2 implémentations différentes de cette interface sans destructeur virtuel et une factory (désolé je ne connais pas le terme consacré en Français):
return Type ? new Implementation1 : new Implementation2;
}
Là on voit tout l'intéret de l'interface. En effet le main n'a pas à connaître les implémentations (je les ai mises à dessein après lui). Je pense qu'en corrigeant les erreurs de syntaxe que j'ai du laisser, vu que je n'ai pas essayé de le compiler, le programme devrait fonctionner au détail près que le destructeurs des classes Implementation1 et Implementation2 ne sont PAS appelés. En comprenant pourquoi, on comprend aussi pourquoi il est INDISPENSABLE de déclarer un destructeur virtuel dans presque toute interface (la seule exception serait de savoir à priori qu'aucune implémentation n'a de destructeur ). Et comme il est dans une interface, il devrait être virtuel pur et on retombe indéfiniment sur mon problème qu'on est obligé en C++ de lui caser quand même une implémentation.
En JAVA qui a été mieux pensé de ce côté là il n'y a évidemment pas ce soucis.
Ce qu'une interface n'a JAMAIS (là c'est un absolu universel ) c'est un constructeur.
D'autre part j'ai essayé la proposition de nickydaquick mais en replaçant les {} par =0 dans l'interface (et en enlevant le constructeur) pour être conforme à l'ennoncé du problème. En effet cela fait 2 fois que l'héritage virtuel est proposé, or c'est quelque chose que j'avoue ne pas trop maîtriser faute de l'utiliser (trop contraignant de devoir se contenter du constructeur par défaut). Et il est vrai que ce mot clé permet d'éviter le double appel des constructeurs et destructeurs d'une classe de base "en double". Le C++ étant friand de ces utilisations du même mot clé à des fins différentes mais vaguement apparentées, j'ai eu l'espoir que cela évite l'appel du destructeur de la sous-classe quand elle est unique ou virtuelle pure. Mais il n'en est rien. Le linker m'a encore injurié après avoir échoué dans sa recherche du destructeur.
J'ai vraiment l'impression que le mot clé virtual dans l'héritage n'a aucune action en dehors de la présence d'une sous-classe commune dans l'héritage multiple (mais je suis près à ce qu'on m'explique le contraire ).
Giles314
Messages postés21Date d'inscriptionsamedi 23 décembre 2006StatutMembreDernière intervention23 avril 2021 22 déc. 2007 à 01:11
*** Je me rends compte qu'en voulant Franciser le nom de ma factory je l'ai renommée de Create en Cree, mais que j'ai gardé le nom original dans les appels. Vous avez rectifié de vous-même je suppose ***