1

'lut,

j'ai un autre problème :

class InterfaceClasseA
{
public:
  virtual void actionA()= 0 ;
}

class ImplementationClasseA
  : public InterfaceClasseA
{
public:
  void actionA() { /*fait qq chose*/ }
}



class InterfaceClasseB
  : public InterfaceClasseA
{
public:
  virtual void actionB()= 0 ;
}

class ImplementationClasseB
  : public InterfaceClasseB, public ImplementationClasseA
{
public
  void actionB() { /* fait autre chose*/ }
}


c'est normal que le compilo ne trouve pas la méthode actionA dans ImplementationClasseB ???
Normalement elle devrait être héritée de ImplementationClasseA, non ?

2

ah ben en fait, mon testcase compile triso

3

'me reste plus qu'à trouver pourquoi mon programme compile pas alors... encore un effet de bord à la c**... #tricouic#

4

ah nan, en fait effectivement j'avais oublié un truc dans mon test :
class InterfaceClasseA 
{ 
public: 
  virtual void actionA()= 0 ; 
} ;
 
class ImplementationClasseA 
{ 
public: 
  void actionA() { /*fait qq chose*/ } 
} ;
 
 
 
class InterfaceClasseB 
  : public InterfaceClasseA 
{ 
public: 
  virtual void actionB()= 0 ; 
} ;
 
class ImplementationClasseB 
  : public InterfaceClasseB, public ImplementationClasseA 
{ 
public:
  void actionB() { /* fait autre chose*/ } 
} ;


int main()
{
	InterfaceClasseB * huhu= new ImplementationClasseB ;
}

5

erreur sur la ligne dans le main :
--------------------Configuration: BDDManager - Win32 Debug--------------------
Compiling...
a.cpp
c:\3dsmax6\maxsdk\samples\bddmanager\a.cpp(32) : error C2259: 'ImplementationClasseB' : cannot instantiate abstract class due to following members:
        c:\3dsmax6\maxsdk\samples\bddmanager\a.cpp(23) : see declaration of 'ImplementationClasseB'
c:\3dsmax6\maxsdk\samples\bddmanager\a.cpp(32) : warning C4259: 'void __thiscall InterfaceClasseA::actionA(void)' : pure virtual function was not defined
        c:\3dsmax6\maxsdk\samples\bddmanager\a.cpp(4) : see declaration of 'actionA'
c:\3dsmax6\maxsdk\samples\bddmanager\a.cpp(32) : error C2259: 'ImplementationClasseB' : cannot instantiate abstract class due to following members:
        c:\3dsmax6\maxsdk\samples\bddmanager\a.cpp(23) : see declaration of 'ImplementationClasseB'
c:\3dsmax6\maxsdk\samples\bddmanager\a.cpp(32) : warning C4259: 'void __thiscall InterfaceClasseA::actionA(void)' : pure virtual function was not defined
        c:\3dsmax6\maxsdk\samples\bddmanager\a.cpp(4) : see declaration of 'actionA'
c:\3dsmax6\maxsdk\samples\bddmanager\a.cpp(33) : warning C4508: 'main' : function should return a value; 'void' return type assumed
Error executing cl.exe.

a.obj - 2 error(s), 3 warning(s)

6

en tous cas, les messages de GCC sont plus lisibles :
$ g++ a.cpp 
a.cpp: Dans function << int main() >>:
a.cpp:32: error: cannot allocate an object of type `ImplementationClasseB'
a.cpp:32: error:   because the following virtual functions are abstract:
a.cpp:4: error:         virtual void InterfaceClasseA::actionA()

7

c'est bien tu fais les questions et les réponses tout seul grin

(par contre si tu utilisais le bouton "éditer" ça serait encore mieux hehe)
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

8

heu, nan, j'ai toujours pas la réponse !! Le véritable test case se trouve au post ./4

(pour éditer, ok, mais des fois je fais ça pour mieux séparer les infos (et aussi c'est une habitude du 56K, c'est plus long d'éditer))


Pen^2 (./4) :
ah nan, en fait effectivement j'avais oublié un truc dans mon test :


sous entendu : j'avais oublié un truc pour que mon test ne compile pas !

9

--------- nan rien en fait, j'ai édité mon post précédent cheeky (mauvaise habitude)

10

ah ok désolé j'ai zappé le post.

je pourrais pas exactement expliquer pourquoi, mais tu as un défaut de conception : InterfaceClasseB dérive de InterfaceClasseA, et ImplementationClasseB dérive de ImplementationClasseA donc de InterfaceClasseA et de InterfaceClasseB donc de InterfaceClasseA (ouf grin), donc elle hérite de deux façons différentes de InterfaceClasseA, donc couille; il faut par exemple que tu vires l'héritage de ta classe InterfaceClasseB et ça devrait compiler.
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

11

oui, mais du coup il n'y a plus de relation explicite entre InterfaceClasseA et InterfaceClasseB... Or InterfaceClasseB est bien un type particulier de InterfaceClasseA. Comment vous procédez d'habitude ?

(En JAVA, j'aurais fait
public class ImplementationClasseB
extends ImplementationClasseA
implements InterfaceClasseB)

12

heu je sais pas, la modélisation dépend de ce que tu veux faire et j'ai pas le contexte de ton application ^^
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

13

En fait c'est simple : ClasseA reprend les méthodes de la classe Statement de JDBC, et ClasseB reprend les méthodes de la classe PreparedStatement (toujours de JDBC)

J'ai fait Statement et PreparedStatement virtuels purs, juste avec les prototypes des méthodes telles qu'elles sont décrites dans l'API JDBC histoire d'avoir une interface indépendante du système de base de données utilisé, avec, actuellement, une implémentation pour PostgreSQL (PQ_Statement et PQ_PreparedStatement).

Comme ça, si j'ai besoin d'un Statement (par exemple), je fais un Statement s*= <recupere_un__PQ_Statement> ;
Et après ça se veut totalement transparent.

14

rien capté, mais "Statement", "PreparedStatement" et "JDBC" ça me parle pas bcp ^^
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

15

désolé wink
JDBC <=> Java Database Connectivity (interface standard de communication générique avec les bases de données pour Java)
API JDBC http://java.sun.com/j2se/1.5.0/docs/api/java/sql/package-summary.html (description de Statement et PreparedStatement)

16

heu... ouais enfin... avec des vrais mots ça s'explique comment ce que tu veux faire ? grin

(nan pske je me fais chier en cours là, ms ça me donne pas pour autant envie d'aller lire de la doc java ^^)
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

17

Nan mais en fait tu n'as pas besoin de savoir ce que sont Statement et PreparedStatement wink
Je vais tacher d'expliquer ça proprement. Un instant.

18

Je souhaite me connecter à une base de données.
Or, les bibliothèques ne sont pas les mêmes selon les bases de données.
Donc je souhaite définir un ensemble de services qui devront me permettre de faire tout ce dont j'ai besoin sur ma base de données. J'ai pensé représenter ces différents services avec un ensemble de classes dont les méthodes sont toutes virtuelles pures.
L'accès aux données sera donc codé différemment selon les moteurs de bases de données (PostgreSQL, MySQL, etc), mais devra par contre _toujours_ utiliser l'interface commune définie précédemment. En effet, grâce à une factorisation de toutes les instanciations des classes spécifiques aux BDD, le changement du moteur de base de données ne devrait donner lieu à aucun changement dans le code source qui utilise l'interface.


ex :
on définit une interface Connection qui doit permettre, entre autres, d'effectuer des requêtes :
class Connection
{
public:
  virtual Resultat executeQuery( string requete ) = 0 ;
}



Ensuite, comme on a décidé d'utiliser MySQL (par exemple), on implémente l'interface Connection avec un accès spécifique à MySQL
class MySQL_Connection
  : public Connection
{
  Resultat executeQuery( string requete )
  {
     /* fait son travail */
  }
}




Du coup, on peut utiliser MySQL_Connection comme une instance de Connection :
Connection* c= new MySQL_Connection ;
//[...]
Resulat r= c->executeQuery("SELECT * FROM TABLE") ;
r.doSomething()
//[...]



On est bien d'accord que, dans le cas où on souhaite passer à PostgreSQL, le seul truc à changer dans le source est la ligne concernant la création de Connection :
Connection* c= new PostgreSQL_Connection ;
//=> le reste ne change pas.








Bref, tout cela fonctionne, le problème c'est que lorsque j'ai besoin de définir une Connection particulière au niveau de mes interfaces :
class SpecialConnection
  : public Connection
{
public:
  virtual void doSomethingSpecial() = 0 ;
}





Dans l'idée, mon implémentation de SpecialConnection (si je me ramène dans le cas ou je travaillais avec MySQL) ressemblerait à ceci :
class MySQL_SpecialConnection
  : public SpecialConnection
{
  Resultat executeQuery( string requete )
  {
     /* fait son travail */
  }
  void doSomethingSpecial()
  {
   /* fait quelque chose de spécial */
  }
}



Mais alors du coup ça m'oblige à copier coller toutes mes méthodes déjà implémentées dans MySQL_Connection dans le source de MySQL_SpecialConnection... C'est pour ça que le plus logique serait de faire ceci :
class MySQL_SpecialConnection
  : public SpecialConnection, MySQL_Connection
{
  void doSomethingSpecial()
  {
   /* fait quelque chose de spécial */
  }
}



Le problème est que cette tentative d'héritage multiple ne fonctionne pas... sad
(j'espère que c'est un peu plus clair)

19

j'avais déjà vu ce problème quelque part, ce que je sais c'est qu'il faut que tu utilises de l'héritage virtuel : http://en.wikipedia.org/wiki/Virtual_inheritance
mais je n'en ai jamais utilisé moi-même
avatar

20

Au pire, ce que je peux faire sans dupliquer le code, c'est de faire une agrégation dans le genre de :
class MySQL_SpecialConnection 
  : public SpecialConnection
{ 
  Resultat executeQuery( string requete ) 
  { 
     return conn.executeQuery(requete) ;
  } 

  void doSomethingSpecial() 
  { 
   /* fait quelque chose de spécial */ 
  } 

  MySQL_Connection conn ;
}


Mais bon c'est un peu dommage, je trouve sad

21

Nue (./19) :
j'avais déjà vu ce problème quelque part, ce que je sais c'est qu'il faut que tu utilises de l'héritage virtuel : http://en.wikipedia.org/wiki/Virtual_inheritance
mais je n'en ai jamais utilisé moi-même

ok merci, je vais regarder smile

22

sinon j'ai une solution qui ne te conviendra pê pas, mais à tout hasard :

class	MonSuperbeWorkaround
{
  // attributs divers
};

class	Connect : public MonSuperbeWorkaround
{
  virtual void	query () = 0;
};

class	Special : public MonSuperbeWorkaround
{
  virtual void	special ()
  {
    // ton truc special
  }
};

class	MySQL : public Connect
{
  virtual void	query ()
  {
    // implémentation de la méthode query
  }
};

class	MySQLSpecial : public MySQL, public Special
{
};
 
int main() 
{ 
  MySQLSpecial* m = new MySQLSpecial ();
}

en gros, tu descends d'un niveau ta méthode "executeQuery" pour ne pas faire hériter ta classe de deux classes qui l'implémentent déjà.

[edit] la solution de Nue est plus mieux, je savais pas qu'on pouvait faire ça en C++ ^^
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

23

Ça fonctionne ! (avec l'héritage virtuel)
Merci bien Nue et Zephyr calin


Pour ceux que ça intéresserait, voici un code qui compile et exécute :
#include <iostream>
#include <string>

class InterfaceClasseA 
{ 
public: 
  virtual void actionA()= 0 ; 
} ;
 
class ImplementationClasseA 
  : virtual public InterfaceClasseA 
{ 
public: 
  void actionA() { std::cout << "actionA\n" ; } 
} ;
 
 
 
class InterfaceClasseB 
  : public virtual InterfaceClasseA 
{
public: 
  virtual void actionB()= 0 ;
} ;

class ImplementationClasseB 
  : public InterfaceClasseB, public ImplementationClasseA 
{ 
public:
  void actionB() { std::cout << "actionB\n" ; } 
} ;


int main( int argc, char* argv[] )
{
  InterfaceClasseA* classeA= new ImplementationClasseA ;
  classeA->actionA() ;

  InterfaceClasseB* classeB= new ImplementationClasseB ;
  classeB->actionA() ;
  classeB->actionB() ;
}



$ g++ a.cpp && ./a.exe 
actionA
actionA
actionB




Zephyr (./22) :
[edit] la solution de Nue est plus mieux, je savais pas qu'on pouvait faire ça en C++ ^^

au moins mes topics servent à quelque chose trioui



Bon, maintenant, faut que je vérifie que ça fonctionne aussi avec VC++ 6 hum
EDIT : bon, ça fonctionne #triway# (le premier warning on se demande à quoi il sert, vu que si on a fait un héritage virtuel c'est probablement pas pour rien, et le second, ben... VC++6 ne respecte pas les standards, mais pour ça, c'est pas trop grave, on lui pardonne hehe)
Deleting intermediate files and output files for project 'TestInheritance - Win32 Debug'.
--------------------Configuration: TestInheritance - Win32 Debug--------------------
Compiling...
a.cpp
c:\3dsmax6\maxsdk\samples\testinheritance\a.cpp(33) : warning C4250: 'ImplementationClasseB' : inherits 'ImplementationClasseA::actionA' via dominance
        c:\3dsmax6\maxsdk\samples\testinheritance\a.cpp(16) : see declaration of 'actionA'
c:\3dsmax6\maxsdk\samples\testinheritance\a.cpp(44) : warning C4508: 'main' : function should return a value; 'void' return type assumed
Linking...

TestInheritance.exe - 0 error(s), 2 warning(s)





PS : petite doc sur l'héritage virtuel en français http://www.bruno-garcia.net/www/Cours/heritage.html#virtuel



PPS : si vous voulez virer le warning "'YYYY' : inherits 'XXX' via dominance" il suffit d'utiliser le code suivant :
#ifdef WIN32
#pragma warning ( disable : 4250 )
#endif

//<placez ici votre héritage virtuel volontaire et connu, dont le warning vous saoûle (dans mon cas, la déclaration de ImplementationClasseB)>

#ifdef WIN32
#pragma warning ( default : 4250 )
#endif

24

Un truc assez simple que je ferais, ce serait rajouter ceci dans ImplemantationClasseB :
void actionA() { ImplementationClasseA::AcstionA(); }
Je n'ai pas testé ni même essayé de compiler, mais c'est généralement un moyen de résoudre les problèmes d'héritage multiples.
Dans les langages supportant la notion d'interfaces (Java, .Net & Co...), cela est implicite.
avatar
Maintenant j'ai la flemme de garder une signature à jour sur ce site. Je n'ai même plus ma chaîne Exec sous la main.

25

Ok, merci. Mais c'est quand même moins élégant que l'héritage virtuel. Bon après il faudra que je creuse, mais à priori, le fait d'hériter de plusieurs classes virtuelles pures ne devrait pas être gênant en théorie... (J'ai lu rapidement un bout de mon bouquin sur le C++, mais je n'ai rien testé)