Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Classe imbriquée et héritage

9 réponses
Avatar
Vincent Richard
Bonjour,

Je suis confronté à un petit problème de syntaxe avec mon code :

template <class T>
class O
{
// ...
};

class A : public O <A::event>
{
public:

class event
{
// ...
};
};

Mais évidemment, le code ne compile pas ("A::event non déclaré"). Et je ne
peux pas utiliser la solution habituelle qui est de déclarer un "class XXX"
avant l'utilisation. Dans mon cas, ça serait :

class A
{
class event;
};

class A : public O <A::event>
{
public:

class event
{
// ...
};
};

qui ne compile bien entendu pas (redéfinition de A). Un "class A::event;" ne
compile évidemment pas non plus...

Quelqu'un a-t-il une idée de comment faire, sachant que je souhaite garder
"event" comme une classe imbriquée dans A (pour des raisons de facilité
d'utilisation et de lisibilité ("event" concerne A)) ?

Merci d'avance pour vos réponses.

Vincent

--
SL> Au fait elle est mieux ma signature maintenant ?
Oui. T'enlève encore les conneries que t'as écrit dedans et c'est bon.
-+- JB in <http://www.le-gnu.net> : Le neuneuttoyage par le vide -+-

9 réponses

Avatar
Gabriel Dos Reis
--=-=- Content-Type: text/plain; charset=iso-8859-15
Content-Transfer-Encoding: quoted-printable

Luc Hermitte writes:

| Ce n'est pas forcément ce que tu attends, mais cela t'en rapprochera un
| peu.
|
| class couche
| {
| class event {...};
| public:
| class A : public O<event> {...};
| };
| using couche::A;

--=-=- Content-Type: text/plain; charset=iso-8859-1
Content-Transfer-Encoding: 8bit


L'as-tu essayé ?

(cela ne peut pas marcher).

-- Gaby

--=-=-=--
Avatar
Vincent Richard

Hmm. Je ne comprends pas vraiment ce que tu veux faire avec ça -- mais
je suppose que je ne suis pas censé comprendre :-)

Tu veux utiliser un nom avant de l'avoir déclarer. Pour moi, cela
indique un problème dans la conception.


Je ne pense pas que le problème vienne d'une erreur de conception. En fait,
c'est plutôt une question de style : je tiens à laisser la classe "event"
dans A. :-)

Explication complète de mon problème : je dispose d'une classe template
"observable" qui est utilisée dans un système de notification :

template <class EVENT_TYPE>
class observable
{
protected:

virtual ~observable() { }

void notify(const EVENT_TYPE& event);

// [...]
};

J'ai également plusieurs classes héritant de "observable" qui peuvent
émettre chacune leur type d'évènement propre :

class A : public observable <A::event>
{
public:

class event { /* ... */ }
};

class B : public observable <B::event>
{
public:

class event { /* ... */ }
};

// ...etc...

Evidemment le code ci-dessus ne compile pas, et c'est bien là le problème...

D'ailleurs, je ne comprends pas pourquoi C++ n'autorise pas le code montré
précédemment : étant donné que la déclaration de la classe n'est pas encore
terminée ('};' final), les choses manquantes peuvent encore être déclarées.
Dans le cas de l'héritage, je ne pense pas que ça pose un quelconque
problème.

Le compilateur pourrait par exemple faire une première "passe" sur le code
de déclaration de la classe, puis une deuxième en s'assurant cette fois-ci
que tout est bien connu... bon, je suis pas développeur de compilo ! :-)

Vincent

--
SL> Au fait elle est mieux ma signature maintenant ?
Oui. T'enlève encore les conneries que t'as écrit dedans et c'est bon.
-+- JB in <http://www.le-gnu.net> : Le neuneuttoyage par le vide -+-

Avatar
d4RK
On 28 Jul 2003, 15:03:37, Luc Hermitte wrote:
Vincent Richard wrote
in news:3f25121c$0$1783$:
Je suis confronté à un petit problème de syntaxe avec mon code :

template <class T>
class O
{
// ...
};

class A : public O <A::event>
{
public:

class event
{
// ...
};
};

Mais évidemment, le code ne compile pas ("A::event non déclaré"). Et
je ne peux pas utiliser la solution habituelle qui est de déclarer un
"class XXX" avant l'utilisation. Dans mon cas, ça serait :

Quelqu'un a-t-il une idée de comment faire, sachant que je souhaite
garder "event" comme une classe imbriquée dans A (pour des raisons de
facilité d'utilisation et de lisibilité ("event" concerne A)) ?



Ce n'est pas forcément ce que tu attends, mais cela t'en rapprochera un
peu.

class couche
{
class event {...};
public:
class A : public O<event> {...};
};
using couche::A;

_____


il est clair qu'il faut laisser la classe 'event' dans la classe
A puisqu'elle en est caractéristique, mais on ne peut pas s'en
sortir en laissant la classe 'observable' template (impossible
à compiler):
j'utiliserais plutôt le polymorphisme pour m'en sortir ici, ie:
declarer une classe baseEvent de laquelle dériveraient toutes
les classes internes 'event' des classes qui en declarent (A
pour l'exemple ci-dessus). Observable redevient une classe non-template
et 'notify' travaille simplement sur les membres communs des
'event[s]'. Par contre étant donné que je ne vois pas trop ce
que fait notify sur son argument, je ne peux pas préciser...

eg: (un truc comme ça :)

class baseEvent{
//membres communs aux events...
//des virtuels donc...
};

class Observable{
//...
void notify(const baseEvent& event);
//...
};

class A:public observable{
public:
class event:public baseEvent{
//...
};
//...
};


Avatar
Luc Hermitte
Gabriel Dos Reis wrote in
news::

| Ce n'est pas forcément ce que tu attends, mais cela t'en rapprochera
| un peu.
|
| class couche {
| class event {...}; public:
| class A : public O<event> {...}; }; using couche::A;


L'as-tu essayé ?


J'avoue que non.

--
Luc Hermitte <hermitte at free.fr>
FAQ de <news:fr.comp.lang.c++> :
<http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ/>
Dejanews : <http://groups.google.com/advanced_group_search>

Avatar
Vincent Richard

[PRECISION]
L'objet "observable" est censée envoyer des évènements, tandis que
l'objet "observer" les reçoit (classique) :

template <class EVENT_TYPE>
class observer
{
virtual ~observer() { }
virtual void update(const observable <EVENT_TYPE>* o,
const EVENT_TYPE& e);
};
[/PRECISION]

il est clair qu'il faut laisser la classe 'event' dans la classe
A puisqu'elle en est caractéristique, mais on ne peut pas s'en
sortir en laissant la classe 'observable' template (impossible
à compiler):
j'utiliserais plutôt le polymorphisme pour m'en sortir ici, ie:
declarer une classe baseEvent de laquelle dériveraient toutes
les classes internes 'event' des classes qui en declarent (A
pour l'exemple ci-dessus). Observable redevient une classe non-template
et 'notify' travaille simplement sur les membres communs des
'event[s]'.


Oui, mais cela empêche d'implémenter "observer" pour plusieurs objets
différents. Tout du moins, cela nécessite de tout regrouper dans
une seule fonction et de tester le type d'évènement (pas très élégant,
à mon goût).

Méthode "template" :

class test : public observer <A::event>, public observer <B::event>
{
void update(const observable <A::event>* o, const A::event& e)
{
// traitement des évènements de A
}

void update(const observable <B::event>* o, const B::event& e)
{
// traitement des évènements de B
}
};

Méthode "non-template" :

class test : public observer
{
// ...
void update(const observable* o, const event& e)
{
if (typeid(e) == typeid(A::event))
{ /* traitement A */ }
else if (typeid(e) == typeid(B::event))
{ /* traitement B */ }
}
};

Par contre étant donné que je ne vois pas trop ce
que fait notify sur son argument, je ne peux pas préciser...


"notify" permet aux classes qui héritent de "observable" d'envoyer
un évènement (l'argument) à chacun des observateurs enregistrés auprès
de l'observé (classe "observer" et std::vector <observer*> dans
l'objet "observable" non présents ici, j'avais simplifié).

Vincent

--
SL> Au fait elle est mieux ma signature maintenant ?
Oui. T'enlève encore les conneries que t'as écrit dedans et c'est bon.
-+- JB in <http://www.le-gnu.net> : Le neuneuttoyage par le vide -+-

Avatar
d4RK
On 28 Jul 2003, 20:17:01, Vincent Richard wrote:

[PRECISION]
L'objet "observable" est censée envoyer des évènements, tandis que
l'objet "observer" les reçoit (classique) :

template <class EVENT_TYPE>
class observer
{
virtual ~observer() { }
virtual void update(const observable <EVENT_TYPE>* o,
const EVENT_TYPE& e);
};
[/PRECISION]

il est clair qu'il faut laisser la classe 'event' dans la classe
A puisqu'elle en est caractéristique, mais on ne peut pas s'en
sortir en laissant la classe 'observable' template (impossible
à compiler):
j'utiliserais plutôt le polymorphisme pour m'en sortir ici, ie:
declarer une classe baseEvent de laquelle dériveraient toutes
les classes internes 'event' des classes qui en declarent (A
pour l'exemple ci-dessus). Observable redevient une classe non-template
et 'notify' travaille simplement sur les membres communs des
'event[s]'.


Oui, mais cela empêche d'implémenter "observer" pour plusieurs objets
différents. Tout du moins, cela nécessite de tout regrouper dans
une seule fonction et de tester le type d'évènement (pas très élégant,
à mon goût).

Méthode "template" :

class test : public observer <A::event>, public observer <B::event>
{
void update(const observable <A::event>* o, const A::event& e)
{
// traitement des évènements de A
}

void update(const observable <B::event>* o, const B::event& e)
{
// traitement des évènements de B
}
};

Méthode "non-template" :

class test : public observer
{
// ...
void update(const observable* o, const event& e)
{
if (typeid(e) == typeid(A::event))
{ /* traitement A */ }
else if (typeid(e) == typeid(B::event))
{ /* traitement B */ }
}
};

Par contre étant donné que je ne vois pas trop ce
que fait notify sur son argument, je ne peux pas préciser...


"notify" permet aux classes qui héritent de "observable" d'envoyer
un évènement (l'argument) à chacun des observateurs enregistrés auprès
de l'observé (classe "observer" et std::vector <observer*> dans
l'objet "observable" non présents ici, j'avais simplifié).

Vincent



Ok, c'est + clair :
J'ai trouvé une solution(?) qui ne t'empêche pas d'avoir un code
qui reste 'propre' mais ça rajoute un objet 'bidon' pour chacune de tes
classes observables, ie:

au lien de faire:

class A:public Observable<A::Event>{...}

qui reste définitivement incompilable, tu peut faire:

class A{
//la classe A normale...
};
class AObservable:public A,public Observable<A::Event>{};

qui rajoute évidemment une classe 'bidon', quoique le tout reste assez
cohérent. (on ne peut pas tout avoir!)

En reprenant une synthèse de tes exemples;
Voila le code simplifié qui doit faire en gros ce que tu veux
(&& qui compile :)

<CODE>
#include <iostream>
using namespace std;

template<class EVENTTYPE> class Observer;

template<class EVENTTYPE>
class Observable{
public:
void registerObserver(Observer<EVENTTYPE>* _observer){
myObserver=_observer;
}
void notify(EVENTTYPE& e){
myObserver->update(*this,e);
}
private:
//normalement un container (vive les listes chainées)
//on se contente d'un champ pour le test :)
Observer<EVENTTYPE>* myObserver;
};

template<class EVENTTYPE>
class Observer{
public:
virtual void update(Observable<EVENTTYPE>& o,EVENTTYPE& e)=0;
};

class A{
public:
class Event{};
Event ping;
};

class B{
public:
class Event{};
};

class AObservable:public A,public Observable<A::Event>{};

class BObservable:public B,public Observable<B::Event>{};

class test:public Observer<A::Event>,public Observer<B::Event>{
public:
void update(Observable<A::Event>& o,A::Event& e){
//traitement...
cout<<"got it! :)"<<endl;
}

void update(Observable<B::Event>& o,B::Event& e){
//traitement...
}
};

int main(void){
test theObserver;
AObservable AO;
BObservable BO;
AO.registerObserver(&theObserver);
BO.registerObserver(&theObserver);
AO.notify(AO.ping);
return 42;
}
</CODE>

(comme tu l'as deviné, tu obtiens 'got it! :)' lors de l'exécution)
Mais, il est impossible qu'il ait un truc qui m'échappe dans
ce que tu veux faire...


Avatar
d4RK
On 28 Jul 2003, 19:51:42, d4RK wrote:

[...]
}
</CODE>

(comme tu l'as deviné, tu obtiens 'got it! :)' lors de l'exécution)
Mais, il est impossible qu'il ait un truc qui m'échappe dans
ce que tu veux faire...


je voulais évidemment dire "il est **possible** qu'il ait qq
chose qui m'échappe " ;)

Avatar
Vincent Richard

J'ai trouvé une solution(?) qui ne t'empêche pas d'avoir un code
qui reste 'propre' mais ça rajoute un objet 'bidon' pour chacune de tes
classes observables, ie:

au lien de faire:

class A:public Observable<A::Event>{...}

qui reste définitivement incompilable, tu peut faire:

class A{
//la classe A normale...
};
class AObservable:public A,public Observable<A::Event>{};

qui rajoute évidemment une classe 'bidon', quoique le tout reste assez
cohérent. (on ne peut pas tout avoir!)


Merci, cette solution me convient. Du point de vue intérieur (code), ce
n'est pas très "élégant", mais ce qui compte, c'est au niveau de
l'utilisateur, et là les exigences sont respectées (modulo le fait
que la classe "bidon" pollue l'espace de noms, mais on ne peut pas
tout avoir...).

Bon, n'empêche que ça serait pas mal que les compilateurs acceptent
cette foutue syntaxe ! ;-)

Vincent

--
SL> Au fait elle est mieux ma signature maintenant ?
Oui. T'enlève encore les conneries que t'as écrit dedans et c'est bon.
-+- JB in <http://www.le-gnu.net> : Le neuneuttoyage par le vide -+-

Avatar
Gabriel Dos Reis
Vincent Richard writes:

| Bon, n'empêche que ça serait pas mal que les compilateurs acceptent
| cette foutue syntaxe ! ;-)

c'est pas demain la veille ;-)

-- Gaby