Methodes virtuelle et virtuelle pure avec le meme nom. Mon compilateur s'embrouille !

Le
vdaanen
Bonjour,

soit une classe abstraite A
class A
{
public :
A() {};
virtual ~A() {};

virtual bool Calcule(double &, double)=0;
virtual bool Calcule(std::vector<double> &, const
std::vector<double> &);
}

et une classe B concrete
class B:public A
{
public :
B() {};
virtual ~B() {};

virtual bool Calcule(double &, double);
}


A l'utilisation :
B b;

b.Calcule(y,0.5); // fonctionne

std::vector<double> l_Yv, l_Xv;
/* init de l_Xv ..*/

b.Calcule(l_Yv, l_Xv);
--> KO, mon compilo me dit "Interpolate' : cannot convert parameter 1
from 'classstd::vector<double>' to 'double &'"

J'ai essayé d'ajouter la définition de virtual bool
Calcule(std::vector<double> &, const std::vector<double> &); dans la
classe B mais au link, le compilo se plait car il ne trouve pas
B::virtual bool Calcule(std::vector<double> &, const
std::vector<double> &)

Mon compilo : MSVC++ 6.0 SP5

Y'a quelque chose qui m'échappe ! Serait-ce un bug du compilo ?

Merci

V
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Fabien LE LEZ
Le #309836
On Tue, 24 Jul 2007 14:46:25 -0000, :

class A
{
public :
virtual bool Calcule(double &, double)=0;
virtual bool Calcule(std::vector<double> &, const std::vector<double> &);


Déclarer une fonction virtuelle pure et une fonction normale avec le
même nom ne me paraît pas une bonne idée dans l'absolu.

}


Il faut un point-virgule ici.


et une classe B concrete
class B:public A
{
public :
B() {};
virtual ~B() {};

virtual bool Calcule(double &, double);


Si tu n'as pas de warning ici, ton compilateur a effectivement un
problème.
En gros, B::Calcule() "cache" tous les A::Calcule().

Cf http://www.gotw.ca/gotw/005.htm

vdaanen
Le #309835
On 24 juil, 17:09, Fabien LE LEZ
On Tue, 24 Jul 2007 14:46:25 -0000, :

class A
{
public :
virtual bool Calcule(double &, double)=0;
virtual bool Calcule(std::vector<double> &, const std::vector<double> &);


Déclarer une fonction virtuelle pure et une fonction normale avec le
même nom ne me paraît pas une bonne idée dans l'absolu.
Les 2 methodes font la meme chose mais l'une sur une donnees de type

double et l'autre sur un vecteur de données.

.... apparemment, je suis bon pour changer le nom de la 2eme :(

}


Il faut un point-virgule ici.
Juste une erreur de re-frappe.


et une classe B concrete
class B:public A
{
public :
B() {};
virtual ~B() {};

virtual bool Calcule(double &, double);


Si tu n'as pas de warning ici, ton compilateur a effectivement un
problème.
En gros, B::Calcule() "cache" tous les A::Calcule().

Oui, c'est egalement mon impression


Cfhttp://www.gotw.ca/gotw/005.htm
merci pour le lien



Fabien LE LEZ
Le #309809
On Tue, 24 Jul 2007 14:46:25 -0000, :

Subject: Methodes virtuelle et virtuelle pure avec le meme nom. Mon compilateur s'embrouille !


Un conseil : évite le mot "méthode" en C++, car sa signification n'est
pas claire. Il y a au moins deux significations possibles : "fonction
membre" et "fonction membre virtuelle".

(Et tant que j'y suis : un "vector<>" est un tableau. Un vecteur est
un objet mathématique, plus proche de std::valarray.)


public :

virtual bool Calcule(double &, double)=0;
virtual bool Calcule(std::vector<double> &, const...


Je viens de relire le chapitre 18 de "Exceptional C++ Style", qui
recommande qu'une fonction membre virtuelle soit être privée.

Et ça résoud au moins partiellement le problème :

class Base
{
public:
bool Calcule (double, double);
bool Calcule (vector<double>, ...);
private:
virtual bool DoCalcule1 (double, double)= 0;
virtual bool DoCalcule2 (vector<double>, ...);
};

Ainsi, tu gardes une interface cohérente (Calcule() accepte des double
ou des vector<double>), tout en permettant aux classes dérivées de
définir DoCalcule1 mais pas DoCalcule2.

James Kanze
Le #309805
On Jul 24, 5:09 pm, Fabien LE LEZ
On Tue, 24 Jul 2007 14:46:25 -0000, :

class A
{
public :
virtual bool Calcule(double &, double)=0;
virtual bool Calcule(std::vector<double> &, const std::vector<double> &);


Déclarer une fonction virtuelle pure et une fonction normale
avec le même nom ne me paraît pas une bonne idée dans
l'absolu.


Ici, les deux fonctions sont virtuelles. A priori, je suppose
qu'il a un traitement par défaut pour la calcule avec des
tableaux, qui se base sur la calcule sur des doubles. (C'est un
peu comme streambuf::xsputn, dont la définition par défaut
renvoie, indirectement, à streambuf::overflow. Et que neuf fois
sur dix, quand tu écris une dérivée de streambuf, il te suffit
de supplanter overflow.)

}


Il faut un point-virgule ici.

et une classe B concrete
class B:public A
{
public :
B() {};
virtual ~B() {};

virtual bool Calcule(double &, double);


Si tu n'as pas de warning ici, ton compilateur a effectivement un
problème.


La norme dit que ce qu'il fait est tout à fait légal. J'avoue
ne jamais avoir eu besoin de réstreindre l'interface comme ça
(c-à-d rendre l'interface de la class dérivée plus étroite que
celle de la classe de base), mais il y a d'autres cas où le
comportement de C++ ici correspond exactement à ce qu'on veut.
(Si les fonctions étaient privées, et non virtuelles, par
exemple, et qu'il existait des conversions entre les
paramètres.)

Dans ce cas-ci, je crois qu'il suffit qu'il ajoute "using
A::Calcule ;" dans la classe dérivée, pour dire au compilateur
qu'on utilise l'interface complète de la classe A, au moins en
ce qui concerne la fonction Calcule.

N'empeche que je crois, comme tu l'as indiqué dans une autre
réponse, que le problème a sa source dans le fait qu'il mélange
l'interface et l'implémentation. En les séparant, il ne doit
rien avoir dans la classe dérivée qui masquerait l'interface
(pas de fonction publique, en dehors des constructeurs et le
destructeur), et que donc, il héritera l'interface complète de
A. Et que tous les appels à l'implémentation auront lieu dans la
classe A, et donc verrait l'« interface d'héritage » (c-à-d
l'ensemble des fonctions virtuelles) complète de la classe A.

Je sens aussi qu'il faut des améliorations dans le vocabulaire.
En fait, une classe de base typique affiche deux interfaces bien
distinctes : une pour les utilisateurs, et une pour les
implémenteurs. La première, celle qu'on entend la plus souvent
par « interface », c'est l'ensemble des éléments publics. La
deuxième, c'est d'une part ce que la classe met à la disposition
des classes dérivées -- les parties protected, la plus souvent
vide -- mais aussi les fonctions virtuelles que la classe
dérivée doit, ou peut, supplanter (et qui seront typiquement
privée). À mon avis, il n'y a que des avantages de maintenir
séparées ses deux interfaces.

Il faudrait dire aussi que le posteur a fait un petit raccourci
dans sa conception. Si j'ai bien compris, ce qu'il a, c'est :

-- une interface abstraite avec deux fonctions,

-- une implémentation « par défaut » d'une de ces fonctions
en fonction de l'autre. C-à-d une implémentation d'une
partie de la fonctionalité (un mixin, plus ou moins), et

-- des implémentations concrètes de l'interface complète, qui
peuvent utiliser l'implémentation par défaut ci-dessus ou
non, comme elles veulent.

Dans la mesure où ces deux premiers points sont distincts,
conceptuellement, au moins, on dirait qu'il faut deux classes
distinctes ; une implémentation concrète qui utilise le défaut
héritera du mixin, en plus que de la classe de base. Ce qui
donnerait quelque chose du genre :

class Interface
{
public:
typedef std::vector< double >
Vector ;
virtual ~Interface() {}
virtual bool calcule( double, double ) = 0 ;
virtual bool calcule( Vector const&,
Vector const& ) = 0 ;
} ;

class DefaultVectorImpl : private virtual Interface
{
public:
virtual bool calcule( Vector const&,
Vector const& ) ;
} ;

class ConcreteUsingDefault
: public virtual Interface
, private DefaultVectorImpl
{
public:
virtual bool calcule( double, double ) ;
} ;

class ConcreteNotUsingDefault
{
public:
virtual bool calcule( double, double ) ;
virtual bool calcule( Vector const&,
Vector const& ) ;
} ;

J'ai simplifié beaucoup ici. Par exemple, je n'ai pas séparé
l'interface utilisateur de l'interface d'héritage (et donc, le
posteur aurait toujours son problème avec ConcreteUsingDefault).
Aussi, en général, quand on se sert des mixins, je préfère qu'il
soit plus ou moins systèmatique ; donc, ici, qu'on définisse
aussi des mixin pour le cas double, et que la classe la plus
dérivée ne définissent tout au moyen des mixins dont elle
hérite. (Note que dans le cas des mixins, la classe la plus
dérivée est en général un template, avec les classes
d'implémentation comme paramètres.)

Mais tout ça est assez lourd, et pour le cas simple, où il
s'agit d'une seule implémentation par défaut d'une des
fonctions, je crois que je resterais avec la forme simplifiée
qu'on avait à l'origine ici (mais probablement avec séparation
de l'interface utilisateur de l'interface d'héritage).

--
James Kanze (GABI Software) email:
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


Fabien LE LEZ
Le #309779
On Wed, 25 Jul 2007 08:43:45 -0000, James Kanze

Si tu n'as pas de warning ici, ton compilateur a effectivement un
problème.


La norme dit que ce qu'il fait est tout à fait légal.


Le compilo a bien un problème (il ne donne pas d'avertissement). Je
n'ai pas parlé de bug.
Je crois que tu parlerais de "qualité d'implémentation".

Peut-être y a-t-il un warning pour ça, activable sur demande.


Publicité
Poster une réponse
Anonyme