OVH Cloud OVH Cloud

Fonctions virtuelles ''globalement pures''

17 réponses
Avatar
Fabien LE LEZ
Bonjour,

Soit la classe :

class C
{
public:
virtual ~C(){}

enum Couleur { ROUGE, VERT };
virtual void f (Couleur couleur)
{
switch (couleur)
{
case ROUGE: f_Rouge(); break;
case VERT: f_Vert(); break;
};
}
virtual void f_Rouge() { f (ROUGE); }
virtual void f_Vert() { f (VERT); }
};

Les fonctions de cette classe sont "globalement virtuelles pures",
i.e. pour que ça marche, une classe dérivée doit redéfinir soit f(),
soit f_Rouge() et f_Vert().

Y a-t-il un moyen de tester cela à la compilation ?

Merci d'avance...

7 réponses

1 2
Avatar
Sylvain
Rémy wrote on 12/06/2006 13:04:

parce que C1 et C2 n'hérite pas virtuellement de C ?



Tout à fait.


et oui, mais pourtant il ne fallait pas le faire...

Donc la solution proposée par Marc est valable.


elle l'est en héritage simple (car elle impose de définir la moitié
désirée des méthodes); elle ne n'est plus en héritage virtuel car une
classe héritant de C1 et C2 regénère exactement le même défaut que la
classe unique d'origine.

Avec toujours la réserve que l'utilisateur doit savoir s'il faut hériter de
C1 ou C2, mais ce n'est peut-être pas un problème.


question focus du problème, je ne suis pas sur d'avoir compris l'intérêt
du risque de Stk Over. ou de devoir choisir entre C1 et C2 simplement
pour fournir des points d'accès publiques multiples (qui devraient donc
appartenir au modèle exposé) !?

un tout bête:

class C {
public:
enum Couleur { ROUGE, VERT };

virtual ~C(){}

void f(Couleur clr) { doIt(clr); }
void f_Rouge() { doIt(ROUGE); }
void f_Vert() { doIt(VERT); }

private:
virtual void doIt(Couleur) = null;
};

ne proposerait-il pas un contrat plus simple et compréhensible ?

Sylvain.



Avatar
kanze
Fabien LE LEZ wrote:

Soit la classe :

class C
{
public:
virtual ~C(){}

enum Couleur { ROUGE, VERT };
virtual void f (Couleur couleur)
{
switch (couleur)
{
case ROUGE: f_Rouge(); break;
case VERT: f_Vert(); break;
};
}
virtual void f_Rouge() { f (ROUGE); }
virtual void f_Vert() { f (VERT); }
};

Les fonctions de cette classe sont "globalement virtuelles
pures", i.e. pour que ça marche, une classe dérivée doit
redéfinir soit f(), soit f_Rouge() et f_Vert().

Y a-t-il un moyen de tester cela à la compilation ?


Est-ce que quelque chose comme le suivant ne ferait-il pas
l'affaire :

template< bool overridesF > class Intermediate ;

class Base
{
public:
virtual ~Base() {}
enum Color { red, green } ;
virtual void f( Color color )
{
switch ( color ) {
case red :
fRed() ;
break ;

case green :
fGreen() ;
break ;
}
}

virtual void fRed() { f( red ) ; }
virtual void fGreen() { f( green ) ; }

private:
template< bool overridesF > friend class Intermediate ;
Base() {}

Base( Base const& ) ; // ou {}, si on veut supporter
// la copie.
} ;

template<>
class Intermediate< true > : public Base
{
public:
virtual void f( Color ) = 0 ;
} ;

template<>
class Intermediate< false > : public Base
{
public:
virtual void fRed() = 0 ;
virtual void fGreen() = 0 ;
} ;

Évidemment, ici, il faut que l'utilisateur dérive d'une des
instantiations de Intermediate : true s'il veut supplanter f,
false pour ne pas supplanter f, mais plutôt les autres
fonctions.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Avatar
Fabien LE LEZ
On 13 Jun 2006 00:51:49 -0700, "kanze" :

template<>
class Intermediate< true > : public Base


Y a-t-il une réelle utilité aux templates ici ?

Je verrais plutôt un

class Intermediate_2_fonctions ;
class Intermediate_1_fonction ;
// Note : ces noms sont assez nuls, il faudra trouver mieux

class Base
{
public:
[...]
private:
friend class Intermediate_2_fonctions ;
friend class Intermediate_1_fonction ;
[...]
} ;

class Intermediate_1_fonction : public Base
{
public:
virtual void f( Color ) = 0 ;
} ;

class Intermediate_2_fonctions: public Base
{
public:
virtual void fRed() = 0 ;
virtual void fGreen() = 0 ;
} ;


En fait, je préfère la solution de Marc
<news:e6jcj9$2tp$ : mettre le code dans les
classes "intermédiaires".

Avatar
kanze
Fabien LE LEZ wrote:
On 13 Jun 2006 00:51:49 -0700, "kanze" :

template<>
class Intermediate< true > : public Base


Y a-t-il une réelle utilité aux templates ici ?


Tirer l'attention sur le parellelisme des deux classes, et
simplifier la documentation (une seule classe à documenter, non
deux).

Je verrais plutôt un

class Intermediate_2_fonctions ;
class Intermediate_1_fonction ;
// Note : ces noms sont assez nuls, il faudra trouver mieux


L'avantage du template, c'est effectivement qu'il n'y a qu'un
seul nom à trouver:-).

[...]

En fait, je préfère la solution de Marc
<news:e6jcj9$2tp$ : mettre le code dans
les classes "intermédiaires".


Moi aussi. Je n'y avais pas pensé ; je me suis laissé trop
conditionné par la formulation de ta question.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
Franck Branjonneau
Fabien LE LEZ écrivait:

On 13 Jun 2006 00:51:49 -0700, "kanze" :

template<>
class Intermediate< true > : public Base


Y a-t-il une réelle utilité aux templates ici ?


Peut-être...

struct Base { // abstraite

enum Couleur { ROUGE, VERT };
virtual void f(Couleur couleur)= 0;
virtual void f_Rouge()= 0;
virtual void f_Vert()= 0;

virtual ~Base()= 0;

};

template< typename _Derived >
struct Basic: virtual Base {

// Ta classe C originale ; elle implémente la logique de f,
// f_Rouge et f_Vert.

virtual void f (Couleur couleur)
{
switch (couleur)
{
case ROUGE: f_Rouge(); break;
case VERT: f_Vert(); break;
};
}
virtual void f_Rouge() { f (ROUGE); }
virtual void f_Vert() { f (VERT); }

};

Deux cas d'usage

struct A: virtual Base, private Basic< A > {

// Je supplante f

virtual void f(Couleur couleur) {

std::cout << "A::fn";
}

};

struct B: virtual Base, private Basic< B > {

// Je supplante f_Rouge et f_Vert

virtual void f_Rouge() {

std::cout << "B::f_Rougen";
}

virtual void f_Vert() {

std::cout << "B::f_Vertn";
}

};

Application

int
main() {

{

Base * base(new A());

base->f(Base::ROUGE);
base->f(Base::VERT);
base->f_Rouge();
base->f_Vert();
}


{

Base * base(new B());

base->f(Base::ROUGE);
base->f(Base::VERT);
base->f_Rouge();
base->f_Vert();
}

}


enum Color =
virtual


Je verrais plutôt un

class Intermediate_2_fonctions ;
class Intermediate_1_fonction ;
// Note : ces noms sont assez nuls, il faudra trouver mieux

class Base
{
public:
[...]
private:
friend class Intermediate_2_fonctions ;
friend class Intermediate_1_fonction ;
[...]
} ;

class Intermediate_1_fonction : public Base
{
public:
virtual void f( Color ) = 0 ;
} ;

class Intermediate_2_fonctions: public Base
{
public:
virtual void fRed() = 0 ;
virtual void fGreen() = 0 ;
} ;


En fait, je préfère la solution de Marc
<news:e6jcj9$2tp$ : mettre le code dans les
classes "intermédiaires".



--
Boujou t'chu tè.
Franck Branjonneau


Avatar
Franck Branjonneau
Fabien LE LEZ écrivait:

On 13 Jun 2006 00:51:49 -0700, "kanze" :

template<>
class Intermediate< true > : public Base


Y a-t-il une réelle utilité aux templates ici ?


Peut-être...

struct Base { // abstraite

enum Couleur { ROUGE, VERT };
virtual void f(Couleur couleur)= 0;
virtual void f_Rouge()= 0;
virtual void f_Vert()= 0;

virtual ~Base()= 0;

};

template< typename _Derived >
struct Basic: virtual Base {

// Ta classe C originale ; elle implémente la logique de f,
// f_Rouge et f_Vert.

virtual void f (Couleur couleur)
{
switch (couleur)
{
case ROUGE: f_Rouge(); break;
case VERT: f_Vert(); break;
};
}
virtual void f_Rouge() { f (ROUGE); }
virtual void f_Vert() { f (VERT); }

};

Deux cas d'usage

struct A: virtual Base, private Basic< A > {

// Je supplante f

virtual void f(Couleur couleur) {

std::cout << "A::fn";
}

};

struct B: virtual Base, private Basic< B > {

// Je supplante f_Rouge et f_Vert

virtual void f_Rouge() {

std::cout << "B::f_Rougen";
}

virtual void f_Vert() {

std::cout << "B::f_Vertn";
}

};

Application

int
main() {

{

Base * base(new A());

base->f(Base::ROUGE);
base->f(Base::VERT);
base->f_Rouge();
base->f_Vert();
}


{

Base * base(new B());

base->f(Base::ROUGE);
base->f(Base::VERT);
base->f_Rouge();
base->f_Vert();
}

}

Je supersede, j'ai oublié de couper les citations inutiles :-(

--
Franck Branjonneau


Avatar
Fabien LE LEZ
On Wed, 14 Jun 2006 12:05:53 +0200, Franck Branjonneau
:


Deux cas d'usage

struct A: virtual Base, private Basic< A > {


Ouais mais bon, ça ne force pas A à implémenter ce qu'il faut :

class A: public Basic<void*>
{
};

Et zou, A::f_Rouge() fait une jolie boucle infinie. Avec A qui hérite
de Base bien sûr.

1 2