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

[long][C++ Windows] Probleme de transtypage

4 réponses
Avatar
Olivier Miakinen
Bonjour,

Je suis en train de faire le portage sur Visual Studio 2005 (ou Visual
Studio 8.0, il me semble que c'est la même chose) d'un immense projet
qui fonctionnait avec Visual C++ 98 (ou Visual C++ 6.0). Je tombe en
particulier sur un bout de code qui me semble assez laid mais qui ne
plantait pas et qui plante maintenant, et j'aimerais savoir ce que je
dois modifier pour qu'il retombe en marche.

Ce code met en ½uvre pas moins de 7 classes différentes, que pour
simplifier je renomme ici A, B, C, D, E, F et G. Les déclarations
sont les suivantes.

class A : public B, C
{ ... };

class B : public D, public E
{ ... };

class C
{ ... };

class D : public F
{ ... };

class E
{ ... };

class F : public G
{ ... };

J'ai tout mis pour être le plus précis possible, mais je suppose que
le problème (que je n'ai pas encore exposé) serait le même avec la
simple déclaration suivante.

class A : public G, C
{ ... };

Dans A, il y a une méthode A::une_methode qui appelle la fonction
une_fonction en lui passant le paramètre this.

A::une_methode()
{
...
une_fonction(this);
...
}

La fonction une_fonction est la suivante.

une_fonction(C *pc)
{
...
G *pg = dynamic_cast<G *>(pc);
...
}

**** LE PROBLÈME ****

Le problème est que pc est non nul, mais que pg est nul. Pourtant, le
débugueur m'indique que pc est bien un pointeur vers un objet de type A,
et A hérite bien d'un (et un seul) objet de type G.

Que puis-je faire ? Je précise que j'ai le droit de modifier les
déclarations et définitions des premières classes, en particulier
A, B et C, et aussi de A::une_methode et de une_fonction.

Par exemple, est-ce que déclarer que C hérite aussi de G résoudrait le
problème ? Il faudrait bien sûr qu'il y ait un seul G au final dans A,
mais je ne sais pas comment déclarer cela.

Cordialement,
--
Olivier Miakinen

4 réponses

Avatar
Franck Branjonneau
Olivier Miakinen <om+ écrivait:

J'ai tout mis pour être le plus précis possible, mais je suppose que
le problème (que je n'ai pas encore exposé) serait le même avec la
simple déclaration suivante.

class A : public G, C
{ ... };

[ Code équivalent à

A a;
C * pc(&a);
G * pg(dynamic_cast< G * >(pc));

]

**** LE PROBLÈME ****

Le problème est que pc est non nul, mais que pg est nul.


G et C sont-ils polymorphes ?

Pourtant, le débugueur m'indique que pc est bien un pointeur vers un objet
de type A, et A hérite bien d'un (et un seul) objet de type G.

Par exemple, est-ce que déclarer que C hérite aussi de G résoudrait le
problème ? Il faudrait bien sûr qu'il y ait un seul G au final dans A,
mais je ne sais pas comment déclarer cela.


L'héritage en diamant c'est :

class Base { public: virtual ~Base(); };

class Left: public virtual Base {};
class Right: public virtual Base {};

class Derived: public Left, public Right {};

Derived "a" une unique Base.

--
Franck Branjonneau

Avatar
Olivier Miakinen

J'ai tout mis pour être le plus précis possible, mais je suppose que
le problème (que je n'ai pas encore exposé) serait le même avec la
simple déclaration suivante.

class A : public G, C
{ ... };


[ Code équivalent à

A a;
C * pc(&a);
G * pg(dynamic_cast< G * >(pc));

]


Oui, en effet.

**** LE PROBLÈME ****

Le problème est que pc est non nul, mais que pg est nul.


G et C sont-ils polymorphes ?


Je dois le vérifier, mais j'espère que oui. Si ce n'est pas le cas pour
C cela peut s'arranger facilement. Malheureusement, si ce n'est pas le
cas pour les classes D, F, G je ne pourrai rien y faire : il s'agit de
classes fournies par les MFC de Microsoft, la dernière étant CWnd.

Je regarde ça demain, de retour sur mon lieu de travail.

Par exemple, est-ce que déclarer que C hérite aussi de G résoudrait le
problème ? Il faudrait bien sûr qu'il y ait un seul G au final dans A,
mais je ne sais pas comment déclarer cela.


L'héritage en diamant c'est :

class Base { public: virtual ~Base(); };

class Left: public virtual Base {};
class Right: public virtual Base {};

class Derived: public Left, public Right {};

Derived "a" une unique Base.


Et si à la place de Left j'ai une chaîne de classes Left1, Left2, Left3,
toutes doivent être polymorphes ? Est-ce que le « virtual » dans la
déclaration de l'héritage est obligatoire aussi ? Si oui, c'est perdu :
j'ai écrit les déclarations telles qu'elles sont, et je ne peux rien
faire pour changer les classes des MFC.

Cela dit, comment expliquer que le transtypage fonctionnait en VC++ 6.0
et ne fonctionne plus en Visual Studio 8.0 ?


Avatar
Michel Decima

Cela dit, comment expliquer que le transtypage fonctionnait en VC++ 6.0
et ne fonctionne plus en Visual Studio 8.0 ?


J'ai essaye le code suivant, qui correspond a la version simplifiee avec
trois classes seulement, sur les compilateurs que j'ai a disposition ici
(xlC-6 et g++-4.1) :

#include <iostream>

class G {
public:
virtual ~G() {}
};

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

class A : public G, C {
public:
void une_methode();
};

void une_fonction(C *pc)
{
G *pg = dynamic_cast<G *>(pc);
std::cout << pc << " " << pg << std::endl;
}

void A::une_methode() {
une_fonction( this );
}

int main() {
A a;
a.une_methode();
}

Ce programme me donne toujours une valeur nulle pour pg:

$ xlC transtypage.cpp && ./a.out
2ff21fb4 0

$ xlC -qlanglvl=strict98 transtypage.cpp && ./a.out
2ff21fb4 0

$ g++ transtypage.cpp && ./a.out
0x2ff21fa4 0

$ g++ -std=c++98 transtypage.cpp && ./a.out
0x2ff21fa4 0

Element interessant, xlC me signale que la classe A dérive de manière
privée de C, puisque c'est le comportement par défaut quand on ne
precise rien:

"transtypage.cpp", line 13.22: 1540-0198 (W) The omitted keyword
"private" is assumed for base class "C".

Juste pour faire taire le warning, je change la declaration de A qui va
maintenant deriver en public de C:

class A : public G, public C {
public:
void une_methode();
};

Et les tests donnent:

$ xlC transtypage.cpp && ./a.out
2ff21fa4 0

$ xlC -qlanglvl=strict98 transtypage.cpp && ./a.out
2ff21fa4 2ff21fa0

$ g++ transtypage.cpp && ./a.out
0x2ff21f94 0x2ff21f90

$ g++ -std=c++98 transtypage.cpp && ./a.out
0x2ff21f94 0x2ff21f90

Suite a la modification, xlC produit une valeur nulle en mode par
defaut, mais non nulle en mode standard. Pour g++, on a une valeur non
nulle dans les deux cas.

Bon, je n'explique pas la situation, mais il y aurait peut etre une
solution a ton probleme si tu peux passer la derivation de C en public
(dans le cas bien sur ou l'absence de mot-cle dans le code d'origine
n'est pas une coquille).

MD.

Avatar
Olivier Miakinen

[...]

Juste pour faire taire le warning, je change la declaration de A qui va
maintenant deriver en public de C:

[...]

Suite a la modification, xlC produit une valeur nulle en mode par
defaut, mais non nulle en mode standard. Pour g++, on a une valeur non
nulle dans les deux cas.

Bon, je n'explique pas la situation, mais il y aurait peut etre une
solution a ton probleme si tu peux passer la derivation de C en public
(dans le cas bien sur ou l'absence de mot-cle dans le code d'origine
n'est pas une coquille).


L'absence du mot-clé « public » dans le code d'origine n'était pas une
coquille de ma part. Je viens de le rajouter, et en effet ça fonctionne
parfaitement maintenant !

Je suis toujours intéressé par une explication si quelqu'un en a une,
mais au moins je peux continuer à avancer sur le reste. Un grand merci
pour cela.