OVH Cloud OVH Cloud

mutation d'instance (héritage multiple)

23 réponses
Avatar
C.B.
Bonjour,
voici mon problème (extrait d'un exam) (utilisant de l'héritage multiple)

J'ai une classe de Base, et deux classes Deriv1 et Deriv2 qui en dérivent.
Ces deux classes Deriv1 et Deriv2 ont des propriétés distinctes, modélisées
ici juste par un attribut _prop (string) de la classe Base, et les méthodes
getProp() des deux classes (qui affichent de quelle classe Deriv1 ou Deriv2
elles sont appelées, et affichent ensuite prop).

J'ai une classe Deriv1Et2 qui dérive de Deriv1 et Deriv2. Je veux que cette
classe ait tantot les propriétés de Deriv1, tantot les propriétés de
Deriv2. On utilise pour cela un type enuméré MODE, et la méthode mutation()
qui va changer de mode. Ensuite, dans la méthode getProp de Deriv1Et2,
suivant le mode, j'appelle Deriv1::getProp() et Deriv2::getProp().

Ceci pour modéliser la mutation d'instance : si Deriv1Et2 a les propriétés
de Deriv1, j'appelle mutation(), elle a les propriétés de Deriv2.
(j'espère etre assez clair, mais le pb est assez compliqué a saisir)

Voici le code, qui compile sans erreurs :

/******** testVirtual.cpp

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

class Base {
protected:
string _prop;
public:
Base() : _prop("") {}
Base(string p) : _prop(p) {}
};

class Deriv1 : virtual public Base {
public:
Deriv1() { _prop = ""; }
Deriv1(string p) : Base(p) {}

virtual string getProp();
};

string Deriv1::getProp() {
cout << "props de Deriv1 : " << _prop;
}

class Deriv2 : virtual public Base {
public:
Deriv2() { _prop = ""; }
Deriv2(string p) : Base(p) {}

virtual string getProp();
};

string Deriv2::getProp() {
cout << "props de Deriv2 : " << _prop;
}

typedef enum MODE {DERIV1, DERIV2};

class Deriv1Et2 : public Deriv1, public Deriv2 {
private:
MODE _m;
public:
Deriv1Et2() : _m(DERIV1) {}
Deriv1Et2(string p) : Deriv1(p), _m(DERIV1) {}

virtual string getProp();
void mutation();
};

void Deriv1Et2::mutation() {
if (_m == DERIV1) { _m = DERIV2; } else { _m = DERIV1; }
}

string Deriv1Et2::getProp() {
if (_m == DERIV1) {
Deriv1::getProp();
}
else {
Deriv2::getProp();
}
}

int main(int argc, char** argv) {
Deriv1Et2 d1;
Deriv1Et2 d2("Mes props");

cout << "au départ : " << endl
<< "d1 : " << d1.getProp() << endl
<< "d2 : " << d2.getProp() << endl;

d1.mutation();
d2.mutation();

cout << "après mutation 1 : " << endl
<< "d1 : " << d1.getProp() << endl
<< "d2 : " << d2.getProp() << endl;

d1.mutation();
d2.mutation();

cout << "après mutation 2 : " << endl
<< "d1 : " << d1.getProp() << endl
<< "d2 : " << d2.getProp() << endl;
}

***************************/

Et voici le résultat que j'obtiens :
*** glibc detected *** free(): invalid pointer: 0xb7e7afe8 ***
props de Deriv1 : Abandon

En changeant l'héritage virtuel des classes Deriv1 et Deriv2 et héritage
normal, j'obtiens :
*** glibc detected *** free(): invalid pointer: 0xb7dfefe8 ***
props de Deriv1 : Mes propsAbandon

Ici l'affiche de _prop se passe bien (mais ça ne marche toujours pas).
Quelqu'un aurait une piste, une idée ?

Merci d'avance

10 réponses

1 2 3
Avatar
Fabien LE LEZ
On Sun, 22 Jan 2006 13:39:22 +0100, "C.B." :

string Deriv1::getProp() {
cout << "props de Deriv1 : " << _prop;
}


Le type de retour étant (std::)string, tu dois renvoyer un string.

string Deriv2::getProp() {
cout << "props de Deriv2 : " << _prop;
}


Idem.

string Deriv1Et2::getProp() {


Idem.

typedef enum MODE {DERIV1, DERIV2};


Ça compile sans warning, ça ?!
Le "typedef" est ici une bizarrerie du langage C (et encore, je ne
suis même pas sûr que ce soit correct). Enlève-le.

Avatar
Fabien LE LEZ
On Sun, 22 Jan 2006 13:39:22 +0100, "C.B." :

string Deriv1::getProp() {
cout << "props de Deriv1 : " << _prop;
}


Au fait, quel compilateur utilises-tu ? Il doit être bien mauvais s'il
ne donne pas au moins un warning en voyant ça.

D'ailleurs, pourquoi est-ce autorisé ?

Avatar
Stéphane Wirtel
vu qu'il emploit la glibc, ça doit certainement être gcc.

Mais cela m'étonne qu'il compile sans problèmes.
Avatar
Fabien LE LEZ
On 22 Jan 2006 06:50:35 -0800, "Stéphane Wirtel"
:

Mais cela m'étonne qu'il compile sans problèmes.


Le fait est que Comeau acceptait de compiler ce code. Avec 4 warnings,
mais pas d'erreur.

Avatar
kanze
Fabien LE LEZ wrote:
On Sun, 22 Jan 2006 13:39:22 +0100, "C.B."
:

string Deriv1::getProp() {
cout << "props de Deriv1 : " << _prop;
}


Le type de retour étant (std::)string, tu dois renvoyer un
string.


Ce qui pourrait bien expliquer l'erreur qu'il a vu. À la site de
l'appel, on va bien appeler le destructeur de la chaîne qu'il
n'a jamais construite.

[...]
typedef enum MODE {DERIV1, DERIV2};


Ça compile sans warning, ça ?!
Le "typedef" est ici une bizarrerie du langage C (et encore,
je ne suis même pas sûr que ce soit correct). Enlève-le.


Utiliser comme ça, ça ne sert pas en C non plus. Intuitivement,
j'aurais cru que c'était illégal -- le typedef ne rapporte à
rien dans la déclaration. Mais je ne trouve rien dans la norme
qui l'intérdit. (C'est probablement juste une oublie -- ou que
personne dans le comité n'ait même pû imaginer le cas.)

--
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
kanze
Fabien LE LEZ wrote:
On Sun, 22 Jan 2006 13:39:22 +0100, "C.B."
:

string Deriv1::getProp() {
cout << "props de Deriv1 : " << _prop;
}


Au fait, quel compilateur utilises-tu ? Il doit être bien
mauvais s'il ne donne pas au moins un warning en voyant ça.

D'ailleurs, pourquoi est-ce autorisé ?


Parce qu'il fait partie d'une catégorie plus grande d'erreurs
qui ne sont pas, en général, détectable. Remplace les appels à
operator<<() par des appels à une autre fonction, que cette
fonction appel exit() ou abort(), ou lève toujours une
exception, et que toi, en tant que programmeur, tu le sais.

--
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
C.B.
Merci pour toutes vos réponses. Après correction des diverses erreurs (les
méthodes qui ne renvoyaient pas de string) et la suppression de _prop qui
compliquait (éventuellement) le problème (aec les constructeurs), ça
marche. (avec l'héritage virtuel, et sans doute sans héritage virtuel
aussi).

Sinon pour le typedef enum MODE, je pensais qu'il fallait le typedef pour
qu'on puisse déclarer une variable de type MODE, mais en fait ça marche
sans. C'est des restes de C (il faut un typedef en C notamment dans une
struct, quand celle ci a comme membre un pointeur sur cette meme struct,
par exemple pour des élts chaines).

Sinon je compilais avec gcc4.0.1 (sous Mandriva 2006), sans aucun warning
pour le typedef.

a+
Avatar
Fabien LE LEZ
On Mon, 23 Jan 2006 22:47:07 +0100, "C.B." :

Sinon je compilais avec gcc4.0.1 (sous Mandriva 2006), sans aucun warning
pour le typedef.


Assure-toi de compiler avec l'option -Wall (affichage de tous les
warnings).

Avatar
Marc Boyer
Le 23-01-2006, Fabien LE LEZ a écrit :
On Mon, 23 Jan 2006 22:47:07 +0100, "C.B." :

Sinon je compilais avec gcc4.0.1 (sous Mandriva 2006), sans aucun warning
pour le typedef.


Assure-toi de compiler avec l'option -Wall (affichage de tous les
warnings).


-Wall ce n'est pas *tous les warnings* mais *beaucoup de warning*.
Celui que je conseille aussi, c'est -Wuninitialized (qui nécessite -O).
A titre personnel, je rajoute -pedantic -W.
Je pense que tout débutant devrait faire pareil.

Marc Boyer
--
Entre le fort et le faible, c'est la liberte qui opprime et le droit
qui libere. Henri Lacordaire, Dominicain


Avatar
Fabien LE LEZ
On Tue, 24 Jan 2006 08:12:58 +0000 (UTC), Marc Boyer
:

-Wall ce n'est pas *tous les warnings* mais *beaucoup de warning*.


Bvarf. Si je comprends bien, "all" veut autant dire dire "tous" que
(std::)"remove" veut dire "enlever"...

1 2 3