OVH Cloud OVH Cloud

héritage

11 réponses
Avatar
olivier
Bonjour,

Le code suivant marche pas toujours, pouvez-vous me donner la bonne
façon de le coder:

#include <iostream>
#include <cctype>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
using namespace std;


class toto
{
public:
toto();
int b;
void tata();
};

toto::toto()
{
b = 4;
}

void toto::tata()
{
}


class titi: toto
{
public:
void go();
};


void titi::go()
{
cout << "ok" << b << endl;
}


int main(int argc, char *argv[])
{
toto* toto1 = new toto();
titi *titi1 = (titi*) toto1;
titi1->go();
return 0;
}




PS: Je n'ai pas envie de rajouter une fonction virtuelle go() à toto
(en fait, je ne le peux pas, toto étant une librairie à laquelle je n'ai
pas accès).


Merci!
Olivier.

10 réponses

1 2
Avatar
Fabien LE LEZ
On Thu, 26 May 2005 17:29:41 +0200, olivier :

Bon, les "toto" et "titi" c'est bien joli, mais pour savoir qui est
dérivé de qui, accroche-toi...

Je me permets donc de modifier ton code, tout en le corrigeant à
mesure :


class Base
{
public:
Base() : b (4) {}

int b; // Normalement, ce genre de truc est privé, mais bon...

void foo() {}

virtual ~Base() {}
// Si "Base" n'a pas de destructeur virtuel, ça veut dire
qu'elle n'est pas prévue pour être dérivée.
};

class titi: toto
// T'es sûr de vouloir un héritage privé ?

class Derivee: public Base
{
public:
void go() { cout << "ok" << b << endl; }
};

int main()
{
Base* ptr_base= new Base;

Derivee* ptr_derivee= [...] ptr_base;
// Ça, c'est impossible : si on peut convertir un "Derivee*" en
"Base*", l'inverse n'est pas possible !
}



Supposons que tu aies une fonction f qui crée un Derivee* mais renvoie
un Base* :


Base* f()
{
return new Derivee;
}

Là, tu peux le retransformer en Derivee* :

int main()
{
Derivee* ptr_d= dynamic_cast<Derivee*> (f());
ptr_d-> go();
delete ptr_d;
}
Avatar
olivier
Merci pour toutes ces infos.

<
< // Ça, c'est impossible : si on peut convertir un "Derivee*" en
<"Base*", l'inverse n'est pas possible !
<

--> Par quel miracle le code que j'ai mis dans ce post marche alors!!! ?
Etrange...
Dommage que tu dises que c'est impossible, ca me rendrait pourtant bien
service car c'est plus "propre" que de faire une fonction à laquelle je
passe une instance de Base en entrée et qui fasse le traitement de go();
on perd l'intérêt de l'objet...


J'aimerais bien utiliser le code que tu proposes, mais j'ai directement
une instance de Derivee, je suis un peu bloqué avec ta soluce...


Cordialement,
Olivier.



Fabien LE LEZ wrote:
On Thu, 26 May 2005 17:29:41 +0200, olivier :

Bon, les "toto" et "titi" c'est bien joli, mais pour savoir qui est
dérivé de qui, accroche-toi...

Je me permets donc de modifier ton code, tout en le corrigeant à
mesure :


class Base
{
public:
Base() : b (4) {}

int b; // Normalement, ce genre de truc est privé, mais bon...

void foo() {}

virtual ~Base() {}
// Si "Base" n'a pas de destructeur virtuel, ça veut dire
qu'elle n'est pas prévue pour être dérivée.
};

class titi: toto
// T'es sûr de vouloir un héritage privé ?

class Derivee: public Base
{
public:
void go() { cout << "ok" << b << endl; }
};

int main()
{
Base* ptr_base= new Base;

Derivee* ptr_derivee= [...] ptr_base;
// Ça, c'est impossible : si on peut convertir un "Derivee*" en
"Base*", l'inverse n'est pas possible !
}



Supposons que tu aies une fonction f qui crée un Derivee* mais renvoie
un Base* :


Base* f()
{
return new Derivee;
}

Là, tu peux le retransformer en Derivee* :

int main()
{
Derivee* ptr_d= dynamic_cast<Derivee*> (f());
ptr_d-> go();
delete ptr_d;
}



Avatar
Fabien LE LEZ
Tout est là : http://www.giromini.org/usenet-fr/repondre.html
Avatar
olivier
Fabien LE LEZ wrote:
Tout est là : http://www.giromini.org/usenet-fr/repondre.html


Effectivement, désolé pour la mise en forme!

Bon, je n'ai toujours pas compris pourquoi mon code marche...
Merci en tout cas pour vos éclaircissements.

Cordialement,
Olivier.

Avatar
Cyrille
Merci pour toutes ces infos.

<
< // Ça, c'est impossible : si on peut convertir un "Derivee*" en
<"Base*", l'inverse n'est pas possible !
<

--> Par quel miracle le code que j'ai mis dans ce post marche alors!!! ?
Etrange...


Faudrait savoir, tu disais qu'il ne marchait pas toujours.
Là, tu essayes d'utiliser un objet Base comme si c'était un autre objet.
Le comportement d'un tel truc est sans doute indéfini. Ca "marche"
parfois et d'autre fois non, selon le sens du vent, du compilateur et du
système.

Dommage que tu dises que c'est impossible, ca me rendrait pourtant bien
service car c'est plus "propre" que de faire une fonction à laquelle je
passe une instance de Base en entrée et qui fasse le traitement de go();
on perd l'intérêt de l'objet...


J'aimerais bien utiliser le code que tu proposes, mais j'ai directement
une instance de Derivee, je suis un peu bloqué avec ta soluce...


Bon, en gros, tu voudrais étendre les fonctionnalités d'une classe que
tu ne peux pas modifier.
Je suppose qu'il y a une bonne raison pour laquelle tu ne peux pas
écrire "new Derivee()" directement.
Tu ne veux pas utiliser une fonction libre parce que ça ne fait pas
assez objet, bon pourquoi pas.

Alors il y a ça:
class Box
{
public:
Box ( toto * t) : m_toto( t ) {}

void go();
private:
toto * m_toto;
};

void Box::go()
{
cout << "ok" << m_toto->b << endl;
}

// tu utilises comme ça:
int main(int argc, char *argv[])
{
toto* toto1 = new toto();
Box boite(toto1);
boite.go();
}

PS: j'appelle la classe Box et son instance boite mais ça ne correspond
pas à quelque chose, je manquais simplement d'imagination.
PS(2): faudra pas oublier de faire un delete sur l'instance de toto,
hein. Pour bien faire, on peut utiliser boost::shared_pointer, pour être
sûr que Box ne contient pas à un moment un pointeur vers un objet détruit.

--
win the yes need the no to win against the no!

Avatar
Cyrille
Fabien LE LEZ wrote:

Tout est là : http://www.giromini.org/usenet-fr/repondre.html



Effectivement, désolé pour la mise en forme!

Bon, je n'ai toujours pas compris pourquoi mon code marche...


Ca "marche" parce que le cast dans le style du C est permissif,
permettant de convertir un pointeur de la base vers un pointeur de la
dérivée quelque soit le vrai type de l'objet. Pour éviter ce genre de
trucs, utilisez dynamic_cast<>, qui lancera une exception std::bad_cast
dans ce genre de cas.

--
win the yes need the no to win against the no!


Avatar
olivier
Cyrille wrote:


Faudrait savoir, tu disais qu'il ne marchait pas toujours.
Là, tu essayes d'utiliser un objet Base comme si c'était un autre objet.
Le comportement d'un tel truc est sans doute indéfini. Ca "marche"
parfois et d'autre fois non, selon le sens du vent, du compilateur et du
système.



En fait ca marche toujours dans le code que j'ai posté. Ca plante
toujours dans un autre code plus complexe (et encore, je ne suis pas sûr
que le plantage vienne de là).

Faudrait que je regarde avec gvd pour comprendre vraiment ce qui se passe...


Bon, en gros, tu voudrais étendre les fonctionnalités d'une classe que
tu ne peux pas modifier.
Je suppose qu'il y a une bonne raison pour laquelle tu ne peux pas
écrire "new Derivee()" directement.
Tu ne veux pas utiliser une fonction libre parce que ça ne fait pas
assez objet, bon pourquoi pas.

Alors il y a ça:
class Box
{
public:
Box ( toto * t) : m_toto( t ) {}

void go();
private:
toto * m_toto;
};

void Box::go()
{
cout << "ok" << m_toto->b << endl;
}

// tu utilises comme ça:
int main(int argc, char *argv[])
{
toto* toto1 = new toto();
Box boite(toto1);
boite.go();
}

PS: j'appelle la classe Box et son instance boite mais ça ne correspond
pas à quelque chose, je manquais simplement d'imagination.
PS(2): faudra pas oublier de faire un delete sur l'instance de toto,
hein. Pour bien faire, on peut utiliser boost::shared_pointer, pour être
sûr que Box ne contient pas à un moment un pointeur vers un objet détruit.



C'est effectivement peut-être le meilleur compromis.

Merci
Olivier.

Avatar
Fabien LE LEZ
On Thu, 26 May 2005 18:26:00 +0200, olivier :

Bon, je n'ai toujours pas compris pourquoi mon code marche...


Qu'entends-tu par "marcher" exactement ?
Ça compile parce qu'un compilo C++ est très permissif, et accepte de
compiler beaucoup de conneries.
Si le programme a le comportement que tu attendais sur ton PC avec
telle version de tel compilateur, c'est plus par hasard qu'autre
chose.

Avatar
Fabien LE LEZ
On Thu, 26 May 2005 18:29:26 +0200, Cyrille :

int main(int argc, char *argv[])
{
toto* toto1 = new toto();
Box boite(toto1);


Généralement, c'est la classe Box qui s'occupe d'allouer, puis
détruire, *toto1.

Avatar
Horst Kraemer
olivier wrote:

Le code suivant marche pas toujours, pouvez-vous me donner la bonne
façon de le coder:

class toto
{
public:
toto();
int b;
void tata();
};



class titi: toto
{
public:
void go();
};



void titi::go()
{
cout << "ok" << b << endl;
}



int main(int argc, char *argv[])
{
toto* toto1 = new toto();
titi *titi1 = (titi*) toto1;
titi1->go();
return 0;
}

PS: Je n'ai pas envie de rajouter une fonction virtuelle go() à toto
(en fait, je ne le peux pas, toto étant une librairie à laquelle je n'ai
pas accès).


Le problème est que ce code ne fonctionne que par hasard s'il a l'air
de fonctionner. La norme du langage C++ dit que le comportement de ton
programme est indéfini - cela veut dire tout simplement que le
programme n'a pas de sens. C'est une pure perte de temps de vouloir
savoir le *pourquoi*. C'est un programme contre le langage....

Une facon légale de faire marcher le programme est de créer "on the
fly" un objet titi à partir de ton toto qui utilise pour sa
construction un constructeur de copie toto(const toto&)

class titi: toto
{
public:
titi(const toto& t) : toto(t) {}
void go() { cout << "ok" << b << endl; }
};

int main()
{
toto* toto1 = new toto();
titi(*toto1).go();
// ou bien
titi* titi1 = new titi(*toto1);
titi1->go();
return 0;
}

--
Horst

--
Lâche pas la patate!

1 2