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

Problèmes d'héritage

32 réponses
Avatar
No_Name
Bonjour,

Je travaille sur une application en C++ pour laquelle j'ai plusieurs
niveaux d'héritage :

A = Classe mère
Classe B = Hérite de A
Classe C = Hérite de B

Je voudrais pouvoir facilement transformer des objets de type B en
objets de type C et inversément.

Pour passer de C à B, j'imagine qu'un simple cast suffira, sachant
qu'en faisant cela, je perdrai les données qui font la sécificité de C
(puisque le but est de me retrouver avec un objet de type B).

Par contre, je ne vois pas bien comment je peux passer de B à C ?
J'envisageais de doter la classe C d'une fonction permettant de créer
un objet de type C à partir d'un objet de type B, en initialisant les
données supplémentaires à des valeurs par défaut, mais je ne suis pas
complètement sur de cette solution.

Et les fonctions de création d'un objet de type B en objet de type C
doivent elle effectivement être des méthodes de la classe C ?

Merci de vos conseils et suggestions.

10 réponses

1 2 3 4
Avatar
Fabien LE LEZ
On Tue, 27 Jan 2009 12:31:50 +0100, No_Name :

Par contre, pour le passage de C vers B, j'ai testé :
[...]



As-tu pensé à lire les messages contenus dans ce thread ?
Avatar
No_Name
Fabien LE LEZ avait écrit le 1/27/2009 :
On Tue, 27 Jan 2009 12:31:50 +0100, No_Name :

Par contre, pour le passage de C vers B, j'ai testé :
[...]



As-tu pensé à lire les messages contenus dans ce thread ?



C'est bien à partir de vos messages que j'essayais de régler le
problème.

Il semble que cela fonctionne (passage de C à B) avec :

B cq(*cqc); où cqc est un pointeur vers un objet de type C.

Merci de votre aide.
Avatar
pjb
No_Name writes:

Fabien LE LEZ a couché sur son écran :
No_Name wrote:

c'est retransformer mon objet C en objet de type B.





On Tue, 27 Jan 2009 11:40:38 +0100, Michael DOUBEZ
:

Je n'avais pas compris que tu voulais faire du polymorphisme.



J'ai justement l'impression qu'il n'en veut pas -- il veut un objet de
classe B plutôt qu'un B* pointant vers un C.

Enfin bref, le conseil du jour : n'utiliser de pointeurs que si on a
une raison explicite de le faire.



Merci pour les réponses de tous, qui m'ont permis d'aboutir aux
résultats suivants :


Passage de B vers C :

B *cd = new B();
C cdc(*cd);

me donne bien un objet de type C à partir d'un objet de type B (suite
à écriture d'un constructeur par recopie).


Par contre, pour le passage de C vers B, j'ai testé :

B *cq(C);
ou bien : B *cq = static_cast<B *>(cqc); (avec cqc pointant vers un
objet de type C)

mais à chaque fois, d'après le débuggeur, j'obtiens le même résultat :
cq pointe vers un objet bizarre, qui est un objet de type C auquel
s'ajoutent les propriétés d'un objet de type B. Alors que mon but et
d'obtenir un objet de type B à partir d'un objet de type C, en perdant
bien entendu les propriétés supplémentaires qui font que C est une
spécialisation de B.




Normalement, en OO, un objet a une identité.

C'est une dépravation de C++ d'appeler "objet" des valeurs que l'on
copie ou projette n'importe comment, est c'est aussi un grand risque
de bogues.

C c;
B b=c; // dépravé!

Par contre quand on réfère les objets via des pointeurs, on est
vraiment OO, et tout fonctionne bien:

C* c=new C;
B* b=c; // no problem.


Dans ce cas, b et c réfèrent le MÊME objet.

Ça permet de travailler avec un objet d'une sous-classe de B sans
savoir à priori de quelle sous-classe il s'agit (en utilisant les
méthodes virtual, toutes les méthodes devraient être virtual, sinon
c'est un signe de dépravation!), ce qui est un des bénéfices
fondamentaux de la programmtion OO que l'on pert avec les projections.


Si tu as un pointeur vers un B que tu subodore être un C, tu peux le
tester avec dynamic_cast:

B* b=unBouUneSousClass();
C* c=dynamic_cast<C*>(b);
if(c==0){
// c'était pas un C!
}else{
c->messageSpecifiqueAuxC();
}

Tu devrais t'en tenir là.



// début des dépravations:
Maintenant, Si tu veux projeter un objet dont tu as un pointeur, c'est
un peu plus délicat, car il faudrait allouer un nouvel objet sur
lequel effectuer la projection.

C* c=new C();
B* b=new B();
(*b)=(*c); // beuark!


------------------------------------------------------------------------
#include <iostream>

class B {
public:
int b;
B():b(-1){}
B(int ab):b(ab){};
};

class C:public B {
public:
int c;
C(int ab,int ac):B(ab),c(ac){};
C(const B& b):B(b),c(-1){};
C(const B* b):B(*b),c(-1){};
};


int main(){
C* c=new C(1,2);
B* b=new B;
(*b)=(*c);
C* c2=new C(b);
std::cout<<"c "<<c->b<<" "<<c->c<<std::endl;
std::cout<<"b "<<b->b<<std::endl;
std::cout<<"c2 "<<c2->b<<" "<<c2->c<<std::endl;
return(0);
}

/*

SRC="/home/pjb/src/tests-c++/depravation.c++" ; EXE="depravation" ; g++ -g3 -ggdb3 -o ${EXE} ${SRC} && ./${EXE} && echo status = $?
c 1 2
b 1
c2 1 -1
status = 0
*/
------------------------------------------------------------------------

--
__Pascal Bourguignon__
Avatar
Michael DOUBEZ
No_Name wrote:
Fabien LE LEZ a couché sur son écran :
No_Name wrote:

c'est retransformer mon objet C en objet de type B.





On Tue, 27 Jan 2009 11:40:38 +0100, Michael DOUBEZ
:

Je n'avais pas compris que tu voulais faire du polymorphisme.



J'ai justement l'impression qu'il n'en veut pas -- il veut un objet de
classe B plutôt qu'un B* pointant vers un C.

Enfin bref, le conseil du jour : n'utiliser de pointeurs que si on a
une raison explicite de le faire.





[snip]
Par contre, pour le passage de C vers B, j'ai testé :

B *cq(C);
ou bien : B *cq = static_cast<B *>(cqc); (avec cqc pointant vers un
objet de type C)

mais à chaque fois, d'après le débuggeur, j'obtiens le même résultat :
cq pointe vers un objet bizarre, qui est un objet de type C auquel
s'ajoutent les propriétés d'un objet de type B.



Je suis perdu C est un B par héritage donc un objet de type C a les
propriétés d'un objet de type B. Comment les propriétés d'un objet de
type B peut il avoir des propriété *en plus* ?

A moins que tu ne respectes pas le principe de substition de Liskov.

Alors que mon but et
d'obtenir un objet de type B à partir d'un objet de type C, en perdant
bien entendu les propriétés supplémentaires qui font que C est une
spécialisation de B.



Si tu parles des méthodes virtuelles. Dans ce cas, il faut construire un
objet de type B:
B b(C());

--
Michael
Avatar
Fabien LE LEZ
On Tue, 27 Jan 2009 13:01:47 +0100, (Pascal J.
Bourguignon):

C'est une dépravation de C++ d'appeler "objet" des valeurs que l'on
copie ou projette n'importe comment



OK. Oublions les mots "objet" et "classe".

class C {};
C c;

C est un type ; c est une variable de type C.


De toute façon, C++ n'est certainement pas un "langage objet". C'est
un langage qui implémente plusieurs paradigmes à sa propre sauce, pour
le plaisir de faier hurler les puritains de chacun de ces paradigmes.
Le pire, c'est que ça marche !


En C++, tout type a, par défaut, une sémantique de valeur.

On peut décider qu'un type a une sémantique d'identité, parce que ça
correspond bien au problème qu'on veut résoudre.

De même, dans certains cas, et indépendamment de la sémantique
(identité/valeur), on peut utiliser du polymorphisme, parce que ça
correspond bien au problème qu'on veut résoudre. Et on peut choisir
entre polymorphisme dynamique ou statique, non pas pour des raisons
d'idéologies, mais pour des raisons bassement pratiques.

Enfin, dans certains cas, on peut créer un objet avec new. Là encore,
pour des raisons pratiques, et indépendamment des deux notions
ci-dessus.

En bref, en C++, on utilise les outils les plus adaptés pour obtenir
un résultat, sans se préoccuper des idéologues.
Avatar
Fabien LE LEZ
On Tue, 27 Jan 2009 12:59:40 +0100, No_Name :

où cqc est un pointeur vers un objet de type C.



La question plus fondamentale ici est : pourquoi diable as-tu un
pointeur ? Y a-t-il une raison profonde à ça, ou est-ce par hasard ?
Avatar
No_Name
Fabien LE LEZ avait soumis l'idée :
On Tue, 27 Jan 2009 12:59:40 +0100, No_Name :

où cqc est un pointeur vers un objet de type C.



La question plus fondamentale ici est : pourquoi diable as-tu un
pointeur ? Y a-t-il une raison profonde à ça, ou est-ce par hasard ?



Parce que le new() que j'utilise pour créer mon objet me retourne un
pointeur ...
Avatar
No_Name
Après mûre réflexion, Michael DOUBEZ a écrit :
No_Name wrote:
Fabien LE LEZ a couché sur son écran :
No_Name wrote:

c'est retransformer mon objet C en objet de type B.





On Tue, 27 Jan 2009 11:40:38 +0100, Michael DOUBEZ
:

Je n'avais pas compris que tu voulais faire du polymorphisme.



J'ai justement l'impression qu'il n'en veut pas -- il veut un objet de
classe B plutôt qu'un B* pointant vers un C.

Enfin bref, le conseil du jour : n'utiliser de pointeurs que si on a
une raison explicite de le faire.





[snip]
Par contre, pour le passage de C vers B, j'ai testé :

B *cq(C);
ou bien : B *cq = static_cast<B *>(cqc); (avec cqc pointant vers un objet
de type C)

mais à chaque fois, d'après le débuggeur, j'obtiens le même résultat : cq
pointe vers un objet bizarre, qui est un objet de type C auquel s'ajoutent
les propriétés d'un objet de type B.



Je suis perdu C est un B par héritage donc un objet de type C a les
propriétés d'un objet de type B. Comment les propriétés d'un objet de type B
peut il avoir des propriété *en plus* ?




Ma chaine d'héritage est la suivante :

A = Classe mère
B = Hérite de A avec TRUC en plus
C = Hérite de B avec BIDULE en plus

Avec le code que je spécifiais ci dessus pour passer de C à B, au lieu
de me retrouver avec un objet de type B, je me retrouvais avec mon
objet de type C auquel était ajouté TRUC, qui est spécifique à la
classe B.
J'avais donc un objet :

C (qui a TRUC et BIDULE) + TRUC (ajouté lors de ma tentative avec le
code B *cq(C);

Depuis, le problème a été résolu grace au code :

B cq(*cqc); où cq est l'objet résultant (de type B) et cqc un
pointeur vers mon objet initial de type C.

Oilà !
Avatar
No_Name
Pascal J. Bourguignon a utilisé son clavier pour écrire :
No_Name writes:

Fabien LE LEZ a couché sur son écran :
No_Name wrote:

c'est retransformer mon objet C en objet de type B.





On Tue, 27 Jan 2009 11:40:38 +0100, Michael DOUBEZ
:

Je n'avais pas compris que tu voulais faire du polymorphisme.



J'ai justement l'impression qu'il n'en veut pas -- il veut un objet de
classe B plutôt qu'un B* pointant vers un C.

Enfin bref, le conseil du jour : n'utiliser de pointeurs que si on a
une raison explicite de le faire.



Merci pour les réponses de tous, qui m'ont permis d'aboutir aux
résultats suivants :


Passage de B vers C :

B *cd = new B();
C cdc(*cd);

me donne bien un objet de type C à partir d'un objet de type B (suite
à écriture d'un constructeur par recopie).


Par contre, pour le passage de C vers B, j'ai testé :

B *cq(C);
ou bien : B *cq = static_cast<B *>(cqc); (avec cqc pointant vers un
objet de type C)

mais à chaque fois, d'après le débuggeur, j'obtiens le même résultat :
cq pointe vers un objet bizarre, qui est un objet de type C auquel
s'ajoutent les propriétés d'un objet de type B. Alors que mon but et
d'obtenir un objet de type B à partir d'un objet de type C, en perdant
bien entendu les propriétés supplémentaires qui font que C est une
spécialisation de B.




Normalement, en OO, un objet a une identité.

C'est une dépravation de C++ d'appeler "objet" des valeurs que l'on
copie ou projette n'importe comment, est c'est aussi un grand risque
de bogues.



Le but de tout cela est de permettre un "undo", c'est à dire
l'annulation des choix du client.

Disons que le client fait un premier choix. On va donc générer en
mémoire un objet de type B correspondant à son choix.

Ensuite il sélectionne une option. C'est là que je suis amené à passer
d'un objet de type B à un objet de type C, correspondant à cette
option.

Puis le client change d'idée et annule son choix. On doit alors revenir
à un objet de type B, d'où l'option est retirée.

Voilà la raison des manipulations auxquelles je me livre ...
Avatar
No_Name
> Voilà la raison des manipulations auxquelles je me livre ...



Voyez vous éventuellement une autre façon (un autre design ?) de
réaliser le même type de traitements et d'offrir le même genre de
possibilités ?
1 2 3 4