OVH Cloud OVH Cloud

constructeur de copie.

47 réponses
Avatar
mohamed92000
Bonjour à tous,
On m'a appris en c++ il faut tjrs mettre en ouevre la forme canonique
(construcetur de copie, affectation et je crois je ne suis pas sure
constructeur par defaut)bref.
supposons qu'on a les classes suivante:
class base{
base(const base& right)
{
donnéeComune1 = right.donnéeComune1;
}

};
class derivéA : base{
derivéA (const derivéA & right): base (right)
{
donéeSpécifiqueA = right.donéeSpécifiqueA;
}
};
class derivéB : base{
derivéB (const derivéB & right): base (right)
{
donéeSpécifiqueB = right.donéeSpécifiqueB;
}
};
est ce que c'est bien de mettre un constructeur de copie dans la
classe de base ou non. et qu'est ce qui ce passe si on fait :
Base pBase;
derivéA ptrA(pBase);

derivéA ptrA;
derivéB ptrB(ptrA);

même question pour l'opérateur d'affectation.
merci d'avance.

10 réponses

1 2 3 4 5
Avatar
Laurent DELEPINE
mourad wrote:
Bonjour à tous,
On m'a appris en c++ il faut tjrs mettre en ouevre la forme canonique
(construcetur de copie, affectation et je crois je ne suis pas sure
constructeur par defaut)bref.


Il n'est pas utile de creer un constructeur de copie pour toutes les
classes dans la plupart des cas, le constructeur synthetisé fait
l'affaire. Par contre a partir du moment ou tu definis le constructeur
de copie, il definir l'operateur d'affectation.

Le constructeur de copie doit obligatoirement etre defini si tu as un
membre pointeur de façon a assurer la duplication correcte de la zone
pointée. Personnellement je prefere l'utilisation d'une classe pointeur
intelligent qui evite la gestion des directe des pointeurs et rend
inutile le constructeur de copie (et parfois le destructeur).


A+

LD

Avatar
Christophe Lephay
"mourad" a écrit dans le message de
news:
On m'a appris en c++ il faut tjrs mettre en ouevre la forme canonique
(construcetur de copie, affectation et je crois je ne suis pas sure
constructeur par defaut)bref.
supposons qu'on a les classes suivante:
class base{
base(const base& right)
{
donnéeComune1 = right.donnéeComune1;
}

};
class derivéA : base{
derivéA (const derivéA & right): base (right)
{
donéeSpécifiqueA = right.donéeSpécifiqueA;
}
};
class derivéB : base{
derivéB (const derivéB & right): base (right)
{
donéeSpécifiqueB = right.donéeSpécifiqueB;
}
};
est ce que c'est bien de mettre un constructeur de copie dans la
classe de base ou non. et qu'est ce qui ce passe si on fait :
Base pBase;
derivéA ptrA(pBase);

derivéA ptrA;
derivéB ptrB(ptrA);


Les formes canoniques dont tu parles ne se justifient que si tu veux offrir
une sémantique de valeur à tes classes. Offrir une sémantique de valeur
signifie que tu peux les utiliser de la même manière que les types de base.

Dans l'exemple que tu donnes, tu dois avoir des erreurs de compilation.

Lorsqu'on veut manipuler des objets issus de toute une hiérarchie
("polymorphisme"), on est obligé de passer systématiquement par des
références ou des pointeurs sur la classe de base.

Il est relativement peu fréquent de vouloir offrir une sémantique de valeur
à toute une hiérarchie. Souvent, c'est plutôt une erreur de design.
Notemment, le problème est qu'une sémantique de valeur s'accorde très mal
avec le polymorphisme dans la mesure où on est obligé, en C++, de passer via
des références ou des pointeurs (ce qui est diamétralement opposé d'une
sémantique de valeur).

Néansmoins, quand celà est justifié, il existe un certain nombre de
techniques ou idiômes permettant d'offrir une sémantique de valeur à des
objets polymorphes (dont le type peut être de n'importe quelle classe d'une
hiérarchie). Fais des recherches sur l'idôme enveloppe-lettre à ce sujet...

Chris

Avatar
Loïc Joly
Laurent DELEPINE wrote:

Par contre a partir du moment ou tu definis le constructeur
de copie, il definir l'operateur d'affectation.


Et le destructeur aussi.

--
Loïc

Avatar
Laurent DELEPINE
Loïc Joly wrote:

Par contre a partir du moment ou tu definis le constructeur de copie,
il definir l'operateur d'affectation.



Et le destructeur aussi.



En effet.


A+

LD


Avatar
mohamed92000
Il est relativement peu fréquent de vouloir offrir une sémantique de valeur
à toute une hiérarchie. Souvent, c'est plutôt une erreur de design.
Notemment, le problème est qu'une sémantique de valeur s'accorde très mal
avec le polymorphisme dans la mesure où on est obligé, en C++, de passer via
des références ou des pointeurs (ce qui est diamétralement opposé d'une
sémantique de valeur).

Néansmoins, quand celà est justifié, il existe un certain nombre de
techniques ou idiômes permettant d'offrir une sémantique de valeur à des
objets polymorphes (dont le type peut être de n'importe quelle classe d'une
hiérarchie). Fais des recherches sur l'idôme enveloppe-lettre à ce sujet...

Chris
merci,

pardon, je crois que j'ai fais une erreur,je voulais bien parlé de pointeurs :

Base* pBase = new Base(..);
derivéA* ptrA = pBase;

derivéA' * ptrA'= new derivéA (..);
derivéB * ptrB = ptrA';
merci

Avatar
Christophe Lephay
"mourad" a écrit dans le message de
news:
pardon, je crois que j'ai fais une erreur,je voulais bien parlé de
pointeurs :


Base* pBase = new Base(..);
derivéA* ptrA = pBase;


Cri du compilateur...

derivéA' * ptrA'= new derivéA (..);
derivéB * ptrB = ptrA';


Idem, à moins que deriveA soit une classe de base de deriveB...

Chris

Avatar
Zouplaz
Christophe Lephay - :


Les formes canoniques dont tu parles ne se justifient que si tu veux
offrir une sémantique de valeur à tes classes. Offrir une sémantique
de valeur signifie que tu peux les utiliser de la même manière que les
types de base.



Heu, c'est quoi une "sémantique de valeur" ??? ('tin ça sonne bien ça)

Merci !

PS : Si c'est dans la faq tu peux m'envoyer paître ;-)

Avatar
Gabriel Dos Reis
Zouplaz writes:

| Christophe Lephay - :
|
| >
| > Les formes canoniques dont tu parles ne se justifient que si tu veux
| > offrir une sémantique de valeur à tes classes. Offrir une sémantique
| > de valeur signifie que tu peux les utiliser de la même manière que les
| > types de base.
| >
|
| Heu, c'est quoi une "sémantique de valeur" ??? ('tin ça sonne bien ça)

essentiallement la notion de « Assignable » combinée avec
« EqualityComparable » (avec une operator== conceptuelle).

http://www.sgi.com/tech/stl/Assignable.html


-- Gaby
Avatar
Christophe Lephay
"Zouplaz" a écrit dans le message de
news:
Christophe Lephay - :
Les formes canoniques dont tu parles ne se justifient que si tu veux
offrir une sémantique de valeur à tes classes. Offrir une sémantique
de valeur signifie que tu peux les utiliser de la même manière que les
types de base.



Heu, c'est quoi une "sémantique de valeur" ??? ('tin ça sonne bien ça)


Je dis ce que c'est dans le passage que tu cites !

Une façon de mieux cerner une sémantique de valeur, c'est de l'opposer à la
notion d'identité.

Par exemple un objet Client, qui aurait donc une identité spécifique, n'a
pas de sémantique de valeur (sauf erreur de design). D'une manière générale,
les objets réels ont une identité et seuls les objets conceptuels ont une
sémantique de valeur.

Souvent, également, on peut associer une unité aux valeurs (par exemple des
mètres, des octets, etc), alors qu'on ne peut pas le faire avec quelque
chose ayant une identité.

Pour résumer, concrètement, une valeur peut être assignée, copiée, comparée
avec une autre valeur (voir la réponse de Gaby)...

Le problème des pointeurs, c'est qu'ils ont une sémantique de valeur en C++,
mais que, du coup, celà se fait au détriment des objets qu'ils pointent. Par
exemple :

objet * A;
objet * B;

...

Quand tu fais
A = B;
La valeur du pointeur B est assignée (ou copiée) dans A, mais l'objet pointé
ne l'est pas. Dans le cas du polymorphisme, tu es obligé donc de géré
manuellement la copie des données pointées pour offrir une sémantique de
valeur à tes objets polymorphes dans la mesure où le seul moyen d'accéder à
un objet polymorphe se fait à travers son adresse (pointeur ou référence).

Si tu gère des objets qui ont chacun une identité propre (par exemple un
client, une facture, une connection réseau...), tu n'as pas à leur fournir
une sémantique de valeur, et les formes canoniques de l'affectation sont
donc non seulement superflues, mais même fortement contre-indiquées. L'usage
courant dans ce cas consiste d'ailleurs à déclarer privés les constructeurs
de copie et d'affectation et à ne pas les définir.

Chris


Avatar
Zouplaz
Christophe Lephay - :

Par exemple un objet Client, qui aurait donc une identité spécifique,
n'a pas de sémantique de valeur (sauf erreur de design). D'une manière
générale, les objets réels ont une identité et seuls les objets
conceptuels ont une sémantique de valeur.



Qu'est ce qu'un objet conceptuel ? Je comprends bien, grâce à tes
explications, la notion d'objet réel mais pas celle d'objet conceptuel...

Un objet "Rectangle" serait conceptuel ?


Si tu gère des objets qui ont chacun une identité propre (par exemple
un client, une facture, une connection réseau...), tu n'as pas à leur
fournir une sémantique de valeur, et les formes canoniques de
l'affectation sont donc non seulement superflues, mais même fortement
contre-indiquées. L'usage courant dans ce cas consiste d'ailleurs à
déclarer privés les constructeurs de copie et d'affectation et à ne
pas les définir.



Si je comprends bien ça veut dire qu'il n'y a aucune raison d'écrire
clientA = clientB puisque qu'il est proprement illogique affecter à un
client B les "caractéristiques" d'un autre client ? Et que du coup la
déclaration d'un constructeur de copie est une erreur...

Ceci étant, il m'a semblé comprendre (dans un bouquin d'initiation au
C++) qu'un constructeur de copie était toujours implémenté par le
compilateur dans le cas ou je n'en fournirais pas un et que dans ce cas
la copie se faisait "bit à bit". C'est donc pour ça que tu dis "déclarer
privés les constructeurs de copie et d'affectation et à ne pas les
définir" ? On redéfini l'opérateur de copie afin que non seulement il ne
fasse "rien" mais qu'en plus étant privé il soit totalement inaccessible
donc provoquant une erreur de compilation si on tente de l'utiliser ??

Merci pour tes explications

1 2 3 4 5