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

Conflit lorS d'un heritage multiple

5 réponses
Avatar
vincent daanen
Bonjour a tous,

je suis confronte a un pb que je ne comprends pas :

J'ai une classe CImageNd qui definit une interface pour des images n
dimensionnelles (n = 2 ou 3, bcp de choses en commun.)
template <class VALUE, size_t DIM>
class CImageNd
{
.....
}

A partir de cette classe, je veux creer 2 types d'images : les images 2D
et les images3D.
Pour cela, j'utilise des types Array2 et Array3 dont je dispose (libs
externes, non modifiables) et mon interface.

template <class VALUE>
class CImage2D : public CImage <VALUE, 2>, protected CArray2<VALUE>
{

}

Comme je ne veux pas que toutes les méthodes de Array2 (resp Array3)
soient accessibles, j'utilise un heritage protege ; pour l'interface,
j'utilise un heritage public.
Comme ma classe d'interface connait bcp de choses, j'ai des methodes qui
portent le meme nom qu dans les classes Array2(Array3) (par exemple :
methode GetNbElements ou Sizeof)


Voici la facon dont je comprends ce qui se passe si je veux utiliser la
methode GetNbElements() sur une CImage3D

CImage3D vol(512,512,512);
vol.GetNbElements();

/*
GetNbElements est publique dans CImageNd et Array3 mais comme
CImage3D herite de facon publique de CImageNd et de facon protegee de
CImage3D ,
c'est CImageNd::GetNbElements() qui est 'appelee'
*/

or le compilo (MSVC++ 6.0, oui je sais mais j'ai pas le choix :o( ) me
dit :

CImage3D<float>::GetNbElements' is ambiguous
E:\Devel\VolumeInterpolation. avec
Image-nD\test\testVolumeInterpolation_3dFreehand\MathTools.cpp(177) :
warning C4385: could be the 'GetNbElements' in base 'CImage<float,3>' of
class 'CImage3D<float>'
E:\Devel\VolumeInterpolation. avec
Image-nD\test\testVolumeInterpolation_3dFreehand\MathTools.cpp(177) :
warning C4385: or the 'GetNbElements' in base 'CContainer3<float>' of
base 'CArray3<float>' of class 'CImage3D<float>'

Pourquoi le compilo trouve-t-il cela ambigu ??

Dois-je utiliser un heritage multiple virtuel meme si CImageNd et
CArray3 n'ont aucun lien de parente ??

merci de votre aide

Vincent

5 réponses

Avatar
Fabien LE LEZ
On Thu, 21 Sep 2006 15:27:24 +0200, vincent daanen :

Comme je ne veux pas que toutes les méthodes de Array2 (resp Array3)
soient accessibles, j'utilise un heritage protege ; pour l'interface,
j'utilise un heritage public.


Es-tu bien sûr qu'un héritage soit la solution ?
Un objet membre de classe CArray2<VALUE> ne serait-il pas plus
adapté ?

Avatar
vincent daanen
On Thu, 21 Sep 2006 15:27:24 +0200, vincent daanen :


Comme je ne veux pas que toutes les méthodes de Array2 (resp Array3)
soient accessibles, j'utilise un heritage protege ; pour l'interface,
j'utilise un heritage public.



Es-tu bien sûr qu'un héritage soit la solution ?
Un objet membre de classe CArray2<VALUE> ne serait-il pas plus
adapté ?


non car l'objet CArray2<VALUE> dispose d'une mecanisme d'allocation de

memoire et pour eviter les duplications de données (stockage plusieurs
fois des matrices image),
il me faut acceder a certains membres proteges (des pointeurs )


Avatar
kanze
vincent daanen wrote:

je suis confronte a un pb que je ne comprends pas :
J'ai une classe CImageNd qui definit une interface pour des
images n dimensionnelles (n = 2 ou 3, bcp de choses en
commun.) template <class VALUE, size_t DIM>

class CImageNd
{
.....
}

A partir de cette classe, je veux creer 2 types d'images : les
images 2D et les images3D.

Pour cela, j'utilise des types Array2 et Array3 dont je
dispose (libs externes, non modifiables) et mon interface.

template <class VALUE>
class CImage2D : public CImage <VALUE, 2>, protected CArray2<VALU E>
{

}

Comme je ne veux pas que toutes les méthodes de Array2 (resp
Array3) soient accessibles, j'utilise un heritage protege ;
pour l'interface, j'utilise un heritage public.


Voire peut-être private ? (Je n'ai jamais vu un cas où
l'héritage protected se justifiait, mais ça ne veut pas dire
qu'il ne peut pas exister.)

Comme ma classe d'interface connait bcp de choses, j'ai des
methodes qui portent le meme nom qu dans les classes
Array2(Array3) (par exemple : methode GetNbElements ou Sizeof)

Voici la facon dont je comprends ce qui se passe si je veux
utiliser la methode GetNbElements() sur une CImage3D

CImage3D vol(512,512,512);
vol.GetNbElements();

/*
GetNbElements est publique dans CImageNd et Array3 mais comme
CImage3D herite de facon publique de CImageNd et de facon protegee de
CImage3D ,
c'est CImageNd::GetNbElements() qui est 'appelee'
*/


C'est CImage3D.GetNbElements() qui serait appelée. Si elle n'a
pas définie de telle fonction, elle en hérite des classes de
base. De toutes les classes de base ; le type d'héritage n'y
change rien.

or le compilo (MSVC++ 6.0, oui je sais mais j'ai pas le choix
:o( ) me dit :

CImage3D<float>::GetNbElements' is ambiguous
E:DevelVolumeInterpolation. avec
Image-nDtesttestVolumeInterpolation_3dFreehandMathTools.cpp(177) :
warning C4385: could be the 'GetNbElements' in base 'CImage<float,3>' of
class 'CImage3D<float>'
E:DevelVolumeInterpolation. avec
Image-nDtesttestVolumeInterpolation_3dFreehandMathTools.cpp(177) :
warning C4385: or the 'GetNbElements' in base 'CContainer3<float>' of
base 'CArray3<float>' of class 'CImage3D<float>'

Pourquoi le compilo trouve-t-il cela ambigu ??


Parce que ça l'est selon les règles du langage. D'abord, le
compilateur cherche une portée avec une déclaration du nom
cherché. Déjà, s'il ne peut pas trouver une portée unique, c'est
ambigu. On ne va pas plus loin. Évidemment, la présence du nom
dans une portée peut masquer sa présence dans une autre ; s'il
est présent dans une classe dérivée, par exemple, on ne le
trouvera jamais dans une classe de base (de cette dérivée). Mais
ici, si le même nom est présent dans les deux classes de base,
il n'y a pas de masquage. (Il y a des règles supplémentaires qui
peuvent ajouter des noms à cause de la recherche dépendente des
paramètres, mais elles ne s'appliquent pas ici. Et elles ne sont
pas encore implémentées dans VC++6.0 non plus.)

Dois-je utiliser un heritage multiple virtuel meme si CImageNd
et CArray3 n'ont aucun lien de parente ??


Ça pourrait être une solution. Si CImageNd hérite de CArray3, des
symboles dans CImageNd auront toujour précédence. Sinon, selon
le cas : n'utilise que des pointeurs à CImageNd. À partir d'un
pointeur à CImageNd, tu n'arriveras jamais à une fonction dans
CArray3. Et le plus propre : définir tous les symboles que tu
veux avoir visible dans CImage3D dans CImage3D -- une fonction
de renvoie à la fonction réelement voulue ferait l'affaire. Dans
du C++ moderne, il y a même un mechanisme pour faire ça plus
simplement, avec des using, mais je ne crois pas que VC++6.0 le
supporte.

--
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
Sylvain
vincent daanen wrote on 21/09/2006 15:27:
Bonjour a tous,

J'ai une classe CImageNd qui definit une interface pour des images n
dimensionnelles (n = 2 ou 3, bcp de choses en commun.)
template <class VALUE, size_t DIM>
class CImageNd
{
}

je veux creer 2 types d'images : les images 2D et images3D
j'utilise des types Array2 et Array3 dont je dispose (libs externes, ...

template <class VALUE>
class CImage2D : public CImage <VALUE, 2>, protected CArray2<VALUE>
{
}

Comme je ne veux pas que toutes les méthodes de Array2 (resp Array3)
soient accessibles, j'utilise un heritage protege ; pour l'interface,
j'utilise un heritage public.


jusqu'ici tout est logique.

Comme ma classe d'interface connait bcp de choses, j'ai des methodes qui
portent le meme nom qu dans les classes Array2(Array3) (par exemple :
methode GetNbElements ou Sizeof)


à partir de là, nécessairement apparaissent les noeuds ou les élements
"ambiguous".

- soit votre schéma d'héritage et votre définition d'interface sont
pertinents et il est normal d'avoir ces ambiguités (qu'il faut accepter
de lever explicitement),
- soit l'héritage protégé est non idéal et votre interface redondante.

commençons par le seconde hypothèse:
le fait que votre interface (CImage<> dont vous ne devez aucune info)
duplique les prototypes présent dans CArrayN<> parait la première source
des ambiguités, il ""suffirait"" de les renommer pour ne pas avoir ce
souci; évidemment ce n'est surement pas la solution car c'est au niveau
de CImageND que les services (de CImage<> ou de CArrayND<> ou des 2)
prennent leur sens.

revenons à la 1ière hypothèse:
tout d'abord un héritage virtuel ne changerait rien: CImage<> et
CArray<> n'ont pas une super interface en commun.
il faut donc bien lever l'ambiguité, et mon sentiment est souvent que
même s'il n'y a aucune ambiguité, c'est le lieu (parce que celui d'un
héritage multiple) où il est bon de "rewriter" l'interface.

pourquoi? si CImage<> et/ou CArray<> répondaient à tous les besoins, on
pourrait les utiliser séparément ou comme donnée membre d'une structure
donnant tantôt accès à l'un tantôt à l'autre.

ici vous voulez formé un agrégat qui pour son fonctionnement a besoin
des 2, dans un cas extrème les 2 héritages seraient non publiques et
vous définirez un nouveau ensemble de fonctions pour un objet CImageND,
dans votre cas je comprends que CImage<> définit déjà, au moins
partiellement, cette interface.

la première piste possible est de revoir cela pour limiter CImage<> à du
fonctionnel (de l'algorithmie) sans trop caractériser comment cela est
utilisé (la frontière entre les sus-dites 2 hypothèses est parfois
légère) - une raison poussant à cela est que CImage n'est pas une classe
de base plu sou moins abstraite, mais une classe template dont
l'instantiation (par exemple CImage<.., 2> correspond déjà à une
spécialisation.

la seconde piste est de définir une nouvelle API d'utilisation pour les
clients de CImageND, ici vous devez résoudre les ambiguités, en même
temps que vous pouvez vouloir modifier l'API permettant l'utilisation
des libs externes (dont Array2|3) afin de les rendre homogène au reste
du code, et/ou des règles d'entreprise, et/ou vos habitudes.

cette définition passera par la (ré)écriture des accesseurs, voire de
tous les points d'appels, par exemple, il faudra faire figurer:

template <class VALUE>
class CImage2D : public CImage <VALUE, 2>, protected CArray2<VALUE>
{
int countItems() [const] { return CImage<VALUE,2>.GetNbElements; }
}

où, a) à tout le moins, vous réglez l'ambiguité par un appel qualifié,
b) vous redéfinissez optionellement les noms des méthodes.

ce nouveau jeu d'API pourrait lui-même être partagé entre les
différentes classes 2D ou 3D; toutefois, parce que ce sont des classes
templates, elles ne sont pas supposées être échangeables (une function
tierce reçoit une CImage2D& ou une CImage3D&, pas les deux).
si un polymorphisme est attendu entre ces 2 classes (CImage2|3D); votre
modèle d'héritage pourrait être changé en:

class Image {
virtuelles pures définissant l'API d'utilisation
};

template <class VALUE>
class CImage2D : public Image,
protected CImage <VALUE, 2>, protected CArray2<VALUE>
{
implémentation de l'interface Image en invoquant
les services de CImage<> ou CArray<> ou les 2
}

un tel schéma à l'avantage IMHO d'utiliser des templates pour du code
dont l'implémentation est générique par rapport à son type template (une
seule écriture d'un array, etc) tout en offrant à l'utilisateur les
avantages d'une famille de classe (API commune, polymorphisme, etc).

Sylvain.

Avatar
vincent daanen
Bonjour,

vincent daanen wrote:


je suis confronte a un pb que je ne comprends pas :
J'ai une classe CImageNd qui definit une interface pour des
images n dimensionnelles (n = 2 ou 3, bcp de choses en
commun.) template <class VALUE, size_t DIM>




class CImageNd
{
.....
}




A partir de cette classe, je veux creer 2 types d'images : les
images 2D et les images3D.




Pour cela, j'utilise des types Array2 et Array3 dont je
dispose (libs externes, non modifiables) et mon interface.




template <class VALUE>
class CImage2D : public CImage <VALUE, 2>, protected CArray2<VALUE>
{

}




Comme je ne veux pas que toutes les méthodes de Array2 (resp
Array3) soient accessibles, j'utilise un heritage protege ;
pour l'interface, j'utilise un heritage public.



Voire peut-être private ? (Je n'ai jamais vu un cas où
l'héritage protected se justifiait, mais ça ne veut pas dire
qu'il ne peut pas exister.)


je vais essayer mais je ne sais pas si je n'aurais pas a heriter une

fois encore (dans qqes temps) de cette classe ...
Comme ma classe d'interface connait bcp de choses, j'ai des
methodes qui portent le meme nom qu dans les classes
Array2(Array3) (par exemple : methode GetNbElements ou Sizeof)




Voici la facon dont je comprends ce qui se passe si je veux
utiliser la methode GetNbElements() sur une CImage3D




CImage3D vol(512,512,512);
vol.GetNbElements();




/*
GetNbElements est publique dans CImageNd et Array3 mais comme
CImage3D herite de facon publique de CImageNd et de facon protegee de
CImage3D ,
c'est CImageNd::GetNbElements() qui est 'appelee'
*/



C'est CImage3D.GetNbElements() qui serait appelée. Si elle n'a
pas définie de telle fonction, elle en hérite des classes de
base. De toutes les classes de base ; le type d'héritage n'y
change rien.


or le compilo (MSVC++ 6.0, oui je sais mais j'ai pas le choix
:o( ) me dit :




CImage3D<float>::GetNbElements' is ambiguous
E:DevelVolumeInterpolation. avec
Image-nDtesttestVolumeInterpolation_3dFreehandMathTools.cpp(177) :
warning C4385: could be the 'GetNbElements' in base 'CImage<float,3>' of
class 'CImage3D<float>'
E:DevelVolumeInterpolation. avec
Image-nDtesttestVolumeInterpolation_3dFreehandMathTools.cpp(177) :
warning C4385: or the 'GetNbElements' in base 'CContainer3<float>' of
base 'CArray3<float>' of class 'CImage3D<float>'




Pourquoi le compilo trouve-t-il cela ambigu ??



Parce que ça l'est selon les règles du langage. D'abord, le
compilateur cherche une portée avec une déclaration du nom
cherché. Déjà, s'il ne peut pas trouver une portée unique, c'est
ambigu. On ne va pas plus loin. Évidemment, la présence du nom
dans une portée peut masquer sa présence dans une autre ; s'il
est présent dans une classe dérivée, par exemple, on ne le
trouvera jamais dans une classe de base (de cette dérivée). Mais
ici, si le même nom est présent dans les deux classes de base,
il n'y a pas de masquage. (Il y a des règles supplémentaires qui
peuvent ajouter des noms à cause de la recherche dépendente des
paramètres, mais elles ne s'appliquent pas ici. Et elles ne sont
pas encore implémentées dans VC++6.0 non plus.)

Merci pour cette info ! C'est cela qui m'a induit en erreur ! Je pensais

(naïvement) que le compilo appelerait la methode publique grace au type
d'heritage !
Au passage, pourquoi cela n'est-il pas ecrit dans la norme ? Ca parait
assez logique en fait !


Dois-je utiliser un heritage multiple virtuel meme si CImageNd
et CArray3 n'ont aucun lien de parente ??



Ça pourrait être une solution. Si CImageNd hérite de CArray3, des
symboles dans CImageNd auront toujour précédence.
Non, CImageND n'herite pas de CArray3 ! J'ai ecrit une reflexion sans

... reflechir a son bien fonde !

Sinon, selon
le cas : n'utilise que des pointeurs à CImageNd. À partir d'un
pointeur à CImageNd, tu n'arriveras jamais à une fonction dans
CArray3. Et le plus propre : définir tous les symboles que tu
veux avoir visible dans CImage3D dans CImage3D -- une fonction
de renvoie à la fonction réelement voulue ferait l'affaire. Dans
du C++ moderne, il y a même un mechanisme pour faire ça plus
simplement, avec des using, mais je ne crois pas que VC++6.0 le
supporte.

J'ai effectivement utilise des directives using et ca marche tres bien !

--> VC++6 supporte donc !

merci de ton aide

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