OVH Cloud OVH Cloud

operator=

3 réponses
Avatar
blc
Bonjour,

J'ai une classe B qui herite de A.

Je veux surcharger l'operator= de B de maniere a ce que:

std::vector<A*> vector;

B b1;
B b2;

vector.push_back (&b1);
vector.push_back (&b2);

(*vector.front ()) = (*vector.back ()); (*)

appelle l'operateur= de la classe B.

1) Si j'ai dans la classe A:

virtual const A & operator=(const A &right)

et dans la classe B:

virtual const B & operator=(const B &right)

Ca marche pas car l'argument de (*) est tjs un A donc il appelle
A::operator=

2) Si je fais:
Dans la classe B:

virtual const A & operator=(const A &right)

Alors je sais que this est un B mais je ne sais rien sur right!!!

Je pourrais dans ce cas faire un dynamic_cast et si c'est pas bon, je
fais un traitement (erreur dans mon cas) et si c'est bon je fais mon
truc mais je me demande si il y a pas un truc (evident??) que j'aurais
manque...

Merci d'avance,
Benoit

3 réponses

Avatar
Christophe Lephay
"blc" a écrit dans le message de
news:
Bonjour,

J'ai une classe B qui herite de A.

Je veux surcharger l'operator= de B de maniere a ce que:

std::vector<A*> vector;

B b1;
B b2;

vector.push_back (&b1);
vector.push_back (&b2);

(*vector.front ()) = (*vector.back ()); (*)

appelle l'operateur= de la classe B.


Il y a peu de chances que celà se produise, vu que ton conteneur ne contient
pas des B mais des A *...

1) Si j'ai dans la classe A:

virtual const A & operator=(const A &right)

et dans la classe B:

virtual const B & operator=(const B &right)

Ca marche pas car l'argument de (*) est tjs un A donc il appelle
A::operator
En plus, je me demande si il n'y aurait pas des restrictions au sujet d'un

operator= polymorphe (et si c'est le cas, c'est surement corrélé au problème
que tu évoques)...

2) Si je fais:
Dans la classe B:

virtual const A & operator=(const A &right)

Alors je sais que this est un B mais je ne sais rien sur right!!!


Une solution :

class A
{
public:
virtual void assign( const A& ) = 0; // on peut aussi renvoyer un A&
};

class B : public A
{
public:
void assign( const A& right )
{
// c'est peut etre mieux de déporter ce test dans la classe A, quoiqu'il
en soit :
assert( dynamic_cast< const B& >( right ) != 0 );

// faire ce que doit faire l'assignation (ou l'affectation)
}
};

class colis_surprise
{
A * surprise;

public:
colis_surprise( const A * any_good_surprise ) : surprise(
any_good_surprise ) {}
colis_surprise& operator=( const colis_surprise& right )
{
surprise->assign( *right->surprise );
rturn *this;
}
};

std::vector< colis_surprise > v;

B b1;
v.push_back( &b1 );

etc...

cf l'idiome enveloppe-lettre de Coplien, où il est question d'offrir une
sémantique de valeur à des pointeurs. Tu trouveras notemment pas mal d'infos
postées par James Kanze dans les archives du groupe...

Chris

Avatar
Christophe Lephay
"Loïc Joly" a écrit dans le message de
news:bhe5na$8ec$
template <class T> class ValuePtr
{
T* myPtr;
ValuePtr(T* t) : myPtr(t) {}
ValuePtr(ValuePtr const &vptr) : myPtr(vptr->clone(););
void swap(ValuePtr &vptr) {std::swap(myPtr, vptr.myPtr);}
ValuePtr operator=(ValuePtr const &vptr) {ValuePtr temp(vptr);
temp.swap(this); }
T* operator->(){return myPtr;}
T const * operator->() const {return myPtr;}
};

Ca m'a l'ai assez proche de l'idiome lettre-enveloppe (que je ne connais
pas bien), mais il suffit d'écrire cette classe une fois pour toutes.


C'est vrai que les deux sont assez proches, la différence principale me
semblant être qu'on n'a pas à déréférencer pour accéder à la valeur avec
l'idiome enveloppe-lettre.

Dans l'idiome enveloppe-lettre tel que je me l'imagine, la classe enveloppe
est une classe de base pour la (les) classe(s) lettre(s). Je ne sais pas si
c'est une propriété intrinsèque de l'idiome ou une variante, auquel cas
c'est la variante que j'ai retenue du bouquin de Coplien.

class enveloppe
{
enveloppe * pointeur_polymorphe;
...
virtual void fonction() { pointeur_polymorphe->fonction(); }
};

class lettre : public enveloppe
{
...
void fonction() { /* quoique ce soit */ }
};

En fait, avec l'idiome enveloppe-lettre, on applique la fonction membre sur
l'enveloppe, qui la répercute sur la lettre, alors qu'un pointeur
intelligent t'oblige à déréférencer pour appeler la fonction membre (et
reste donc un pointeur, même si il et intelligent).

Comme le soulignait james dans un post pas si lointain, le problème est
aussi souvent résolu par une classe proxy (dans laquelle la classe manipulée
n'a pas à appartenir à la même hiérarchie que la classe masquée), qui
répercute aussi automatiquement l'appel de la fonction membre sur le
pointeur masqué.

Une différence évidente entre l'idiome enveloppe-lettre et l'idiome proxy,
c'est que c'est la classe gérante qui impose la sémantique(*) à la classe
gérée pour le premier, alors que c'est le contraire avec le second(**).
L'inconvénient principal de la classe enveloppe-lettre est de ne pas pouvoir
être utilisé à postériori (inapplicable en l'état sur des classes déjà
existantes).

Dans le cas du posteur initial, quoiqu'il en soit, les trois solutions me
semblent valides, remplaçant chacune l'affectation de pointeurs par une
affectation d'objets.

(*) la classe enveloppe ne peut bien évidemment pas être une classe
abstraite si l'objectif est de passer par elle pour avoir une sémantique de
valeurs, d'objets donc. C'est pourquoi j'ai préféré parler de sémantique
plutôt que d'interface
(**) on pourrait presque dire que la classe gérée n'impose rien à la classe
gérante dans la mesure où il n'existe aucune relation d'héritage entre les
deux. D'ailleurs, lorsque les interfaces de la classe proxy et de la classe
"proxied" diffère, ça devient un pont et le compilateur continue sa route en
baillant à s'en décrocher la machoire.

Chris

Avatar
blc
Merci a tous pour vos reponses. J'ai finalement fait une methode assign..

Benoit