OVH Cloud OVH Cloud

Copie de classe dérivée

9 réponses
Avatar
Michael
Bonjour à tous,

soient les classes suivantes:

class base //(classe abstraite)
{
// blabla
};

class deriv1 : public base
{
//blabla
};

class deriv2 : public base
{
//blabla
};


Soit:

std::vector<base*> liste;

Si je prends un iterator quelconque:

std::vector<base*>::const_iterator ITE = ...;

je voudrais faire une copie de ce que contient cet iterateur...

Seulement comment faire ça? est-ce que je dois tester quelque chose du
type:

deriv1 * toto = dynamic_cast<deriv1*>(*ITE);
if (toto != NULL)
toto = *ITE,
else
{
deriv2 * foo = dynamic_cast<deriv2*>(*ITE);
if (foo != NULL)
foo = *ITE;
}

Mais bon si j'ai 15 classes dérivées c'est pas top.

Ou bien est-ce que je peux utiliser des constructeurs par copie pour
chacune des classes dérivées?

Bref, comment faites vous la copie d'une classe dérivée depuis le tye de
base?

9 réponses

Avatar
Ahmed MOHAMED ALI
Bonjour,
if (toto != NULL)
toto = *ITE,
Ce code est inutile.


deriv1 * toto = dynamic_cast<deriv1*>(*ITE);
Je pense que tu veux parler de conversion et non de copy (convertir la

classe de base vers la classe dérivée appelée aussi downcasting)
Si tu veux éviter les if/else ,il faut utiliser des méthode virtuelles.

Ahmed

"Michael" wrote in message
news:
Bonjour à tous,

soient les classes suivantes:

class base //(classe abstraite)
{
// blabla
};

class deriv1 : public base
{
//blabla
};

class deriv2 : public base
{
//blabla
};


Soit:

std::vector<base*> liste;

Si je prends un iterator quelconque:

std::vector<base*>::const_iterator ITE = ...;

je voudrais faire une copie de ce que contient cet iterateur...

Seulement comment faire ça? est-ce que je dois tester quelque chose du
type:

deriv1 * toto = dynamic_cast<deriv1*>(*ITE);
if (toto != NULL)
toto = *ITE,
else
{
deriv2 * foo = dynamic_cast<deriv2*>(*ITE);
if (foo != NULL)
foo = *ITE;
}

Mais bon si j'ai 15 classes dérivées c'est pas top.

Ou bien est-ce que je peux utiliser des constructeurs par copie pour
chacune des classes dérivées?

Bref, comment faites vous la copie d'une classe dérivée depuis le tye de
base?


Avatar
Falk Tannhäuser
Michael wrote:

class base //(classe abstraite)
{
// blabla


public:
virtual base* clone() const = 0;

};

class deriv1 : public base
{
//blabla


public:
virtual base* clone() const { return new deriv1(*this); }

};

class deriv2 : public base
{
//blabla


public:
virtual base* clone() const { return new deriv2(*this); }

};


Bien sûr, cela nécessite un constructeur de copie en état de marche
dans chaque classe.

Si l'hiérarchie d'héritage est très importante et comporte plusieur s
niveaux, il est toutefois possible d'oublier de reimplémenter clone()
dans une des classes dérivées, une erreur risque de passer inaperçu e
à la compilation. C'est pour ça qu'il est préférable de rendre la
fonction clone() non virtuelle dans 'base' et de déléguer à une fon ction
privée virtuelle dont on vérifie le résultat :

class base
{
public:
base* clone() const
{
base* result = do_clone();
assert(typeid(*result) == typeid(*this));
return result;
}

private:
virtual base* do_clone() const = 0;
};


class derivx : public base
{
// Pas de rédéfinition de clone()
private:
virtual base* do_clone() const { return new derivx(*this); }
};


std::vector<base*> liste;
Si je prends un iterator quelconque:
std::vector<base*>::const_iterator ITE = ...;
je voudrais faire une copie de ce que contient cet iterateur...
Seulement comment faire ça? est-ce que je dois tester quelque chose d u
type:

deriv1 * toto = dynamic_cast<deriv1*>(*ITE);


base* toto = (**ite).clone();
ou
base* toto = (*ite)->clone();

Falk

Avatar
Michael
std::vector<base*> liste;
Si je prends un iterator quelconque:
std::vector<base*>::const_iterator ITE = ...;
je voudrais faire une copie de ce que contient cet iterateur...
Seulement comment faire ça? est-ce que je dois tester quelque chose d
u

type:

deriv1 * toto = dynamic_cast<deriv1*>(*ITE);


base* toto = (**ite).clone();
ou
base* toto = (*ite)->clone();


Merci pour ta réponse...

Donc grâce à cette solution, si *ITE est du type deriv2, on pourra transtyper
toto:

base* toto = (*ite)->clone();

en deriv2 sans problème?


Avatar
Michael
Je viens de trouver ça:

http://www.ifrance.com/jlecomte/c++/c++-faq-lite/abcs-fr.html#[22.5]

C'est bien l'idiome du constructeur virtuel dont il s'agit?
Avatar
Falk Tannhäuser
Michael wrote:
Je viens de trouver ça:
http://www.ifrance.com/jlecomte/c++/c++-faq-lite/abcs-fr.html#[22.5]
C'est bien l'idiome du constructeur virtuel dont il s'agit?


Oui, tout à fait.

Falk

Avatar
Michael
Falk Tannhäuser wrote in news:d2jr2v$1m4$1
@s1.news.oleane.net:

Michael wrote:
Je viens de trouver ça:
http://www.ifrance.com/jlecomte/c++/c++-faq-lite/abcs-fr.html#[22.5]
C'est bien l'idiome du constructeur virtuel dont il s'agit?


Oui, tout à fait.

Falk



Je te remercie pour ta réponse, je viens de finir d'implémenter tout ça, et
ça marche vraiment très bien...


Avatar
Michel Michaud
Dans le message d2jjv6$t7d$,
[... Code pour fonction clone à rédéfinir dans chaque classe dérivée]
Si l'hiérarchie d'héritage est très importante et comporte plusieurs
niveaux, il est toutefois possible d'oublier de reimplémenter
clone() dans une des classes dérivées, une erreur risque de passer
inaperçue à la compilation. C'est pour ça qu'il est préférable de
rendre la fonction clone() non virtuelle dans 'base' et de déléguer
à une fonction privée virtuelle dont on vérifie le résultat :


Tant qu'à être rendu là, pourquoi pas un petit coup de CRTP pour ne
pas avoir à rédéfinir manuellement et donc éviter toute possibilité
d'oublier de le faire :

class Base // La vraie base...
{
public :
virtual Base* Clone() const = 0;
// ... autres fonctions
};

template <typename T>
class BasePourDeriver : public Base
{
public :
Base* Clone() const
{
return new T(*static_cast<const T*>(this));
}
// ... (?)
};

class Derive1 : public BasePourDeriver<Derive1>
{
public :
// autres fonctions, mais Clone est hérité...
};

class ClDerive2 : public BasePourDeriver<Derive2>
{
public :
// autres fonctions, mais Clone est hérité...
};

etc.

On pourrait rendre Base inutilisable pour dérivation directe
ailleurs que dans BasePourDeriver, afin d'éviter tout malentendu.
À faire en exercice :-)

--
Michel Michaud
http://www.gdzid.com
FAQ de fr.comp.lang.c++ :
http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ/

Avatar
Falk Tannhäuser
Michel Michaud wrote:
Tant qu'à être rendu là, pourquoi pas un petit coup de CRTP pour ne
pas avoir à rédéfinir manuellement et donc éviter toute possibi lité
d'oublier de le faire :

class Base // La vraie base...
{
public :
virtual Base* Clone() const = 0;
// ... autres fonctions
};

template <typename T>
class BasePourDeriver : public Base
{
public :
Base* Clone() const
{
return new T(*static_cast<const T*>(this));
}
// ... (?)
};

class Derive1 : public BasePourDeriver<Derive1>
{
public :
// autres fonctions, mais Clone est hérité...
};


Cool! Mais dés qu'il y a un niveau supplémentaire d'héritage,
la possibilité d'oubli subsiste :

class PlusDerive1_1 : public Derive1
{
// Oups ! Clone() n'est pas rédéfini :-(
};

Base* pd11 = new PlusDerive1_1;
Base* b = pd11->Clone(); // Grmpfff !
assert(typeid(*b) == typeid(PlusDerive1_1)); // Au revoir...

Falk

Avatar
Michel Michaud
Dans le message d307pc$ngi$,
Michel Michaud wrote:
class Base // La vraie base...
{
public :
virtual Base* Clone() const = 0;
// ... autres fonctions
};

template <typename T>
class BasePourDeriver : public Base
{
public :
Base* Clone() const
{
return new T(*static_cast<const T*>(this));
}
// ... (?)
};

class Derive1 : public BasePourDeriver<Derive1>
{
public :
// autres fonctions, mais Clone est hérité...
};


Cool! Mais dés qu'il y a un niveau supplémentaire d'héritage,
la possibilité d'oubli subsiste :

class PlusDerive1_1 : public Derive1
{
// Oups ! Clone() n'est pas rédéfini :-(
};

Base* pd11 = new PlusDerive1_1;
Base* b = pd11->Clone(); // Grmpfff !
assert(typeid(*b) == typeid(PlusDerive1_1)); // Au revoir...


Mais alors il y aura (normalement) une différence importante
entre cette classe « intermédiaire » et les autres; en principe,
elle serait abstraite aussi par exemple. Lorsque l'on voit cette
différence, il faut alors simplement faire une autre classe
intermédiaire (Dérivé1PourDériver<T>). Mais je vois surtout qu'il
pourrait être intéressant d'intégrer ta solution de vérification
dans le code de la classe de base...

--
Michel Michaud
http://www.gdzid.com
FAQ de fr.comp.lang.c++ :
http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ/