Comment déclarer une interface en C++

Signaler
Messages postés
20
Date d'inscription
samedi 23 décembre 2006
Statut
Membre
Dernière intervention
17 janvier 2016
-
Messages postés
20
Date d'inscription
samedi 23 décembre 2006
Statut
Membre
Dernière intervention
17 janvier 2016
-
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

40 réponses

Messages postés
2023
Date d'inscription
mardi 24 septembre 2002
Statut
Membre
Dernière intervention
28 juillet 2008
5
Je n'ai jamais eu de problème avec le destructeur virtuel pure. Mais dans ton cas, ne pas déclarer le destructeur te poserait un problème ?

class Interface
{
    virtual int Run (void) = 0;
};

Mais pour revenir a ton problème, moi j'en vois un autre:
-> ton constructeur n'est pas accessible car déclaré "private".
Messages postés
20
Date d'inscription
samedi 23 décembre 2006
Statut
Membre
Dernière intervention
17 janvier 2016

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;
}
Messages postés
20
Date d'inscription
samedi 23 décembre 2006
Statut
Membre
Dernière intervention
17 janvier 2016

Ouuups il fallais évidement lire ceci:

int main()
{
    Interface *Programme = new Implementation;
    int Result = Programme.Run();
    delete Programme;
    return Result;
}
Messages postés
966
Date d'inscription
samedi 3 avril 2004
Statut
Membre
Dernière intervention
4 mars 2010
4
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.
Messages postés
2023
Date d'inscription
mardi 24 septembre 2002
Statut
Membre
Dernière intervention
28 juillet 2008
5
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.
Messages postés
2023
Date d'inscription
mardi 24 septembre 2002
Statut
Membre
Dernière intervention
28 juillet 2008
5
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.
Messages postés
1137
Date d'inscription
lundi 17 novembre 2003
Statut
Membre
Dernière intervention
23 janvier 2016
20
Salut,

Pour l'héritage multiple il faut déclarer la dérivation viruelle :

class Implementation : virtual public Interface
{
};
Messages postés
20
Date d'inscription
samedi 23 décembre 2006
Statut
Membre
Dernière intervention
17 janvier 2016

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."
Messages postés
2023
Date d'inscription
mardi 24 septembre 2002
Statut
Membre
Dernière intervention
28 juillet 2008
5
"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.
Messages postés
20
Date d'inscription
samedi 23 décembre 2006
Statut
Membre
Dernière intervention
17 janvier 2016

"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.
Messages postés
966
Date d'inscription
samedi 3 avril 2004
Statut
Membre
Dernière intervention
4 mars 2010
4
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...
Messages postés
20
Date d'inscription
samedi 23 décembre 2006
Statut
Membre
Dernière intervention
17 janvier 2016

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.
Messages postés
2023
Date d'inscription
mardi 24 septembre 2002
Statut
Membre
Dernière intervention
28 juillet 2008
5
Mais j'avais pourtant l'impression d'avoir été clair :)
Une interface en C++ n'a pas de destructeur !

class Interface
{
    virtual int Run() = 0;
};

Aucun besoin de destructeur pour une interface puisqu'elle ne décrit pas un objet.
Messages postés
966
Date d'inscription
samedi 3 avril 2004
Statut
Membre
Dernière intervention
4 mars 2010
4
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...
Messages postés
20
Date d'inscription
samedi 23 décembre 2006
Statut
Membre
Dernière intervention
17 janvier 2016

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.
Messages postés
142
Date d'inscription
mardi 17 janvier 2006
Statut
Membre
Dernière intervention
29 août 2009
1
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...
Messages postés
416
Date d'inscription
vendredi 31 janvier 2003
Statut
Membre
Dernière intervention
19 décembre 2013
2
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 ...!
Messages postés
2023
Date d'inscription
mardi 24 septembre 2002
Statut
Membre
Dernière intervention
28 juillet 2008
5
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.

class Interface
{
public:
  virtual void methode() const = 0;
};

class Implementation: public Interface
{
   public:
      Implementation():Interface(){}
      virtual ~Implementation(){}

     virtual void methode() const{}
};

int main()
{
    Implementation Programme;
    return 0;
}

Ca compile, ca link et ca run sans pb (testé).
Messages postés
20
Date d'inscription
samedi 23 décembre 2006
Statut
Membre
Dernière intervention
17 janvier 2016

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):



class Interface
{
public:
  virtual void methode() const = 0;
};

Interface* Cree (bool Type);

int main()
{
    Interface *Programme;
    Programme = Create(False);
    Programme->methode();
    delete Programme;
    Programme = Create(true);

    Programme->methode();

    delete Programme;

    return 0;
}

class Implementation1: public Interface

{

   public:

      Implementation():Interface(){ cout << "Implementation 1" << endl; }

      virtual ~Implementation(){ cout << "Destruction 1" << endl; }


     virtual void methode() const{ cout << "Run 1" << endl; }

};


class Implementation2: public Interface


{


   public:


      Implementation():Interface(){ cout << "Implementation 2" << endl; }


      virtual ~Implementation(){ cout << "Destruction 2" << endl; }



     virtual void methode() const{ cout << "Run 2" << endl; }


};

Interface* Cree (bool Type)

{




    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 ).
Messages postés
20
Date d'inscription
samedi 23 décembre 2006
Statut
Membre
Dernière intervention
17 janvier 2016

*** 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 ***